pax_global_header 0000666 0000000 0000000 00000000064 14170524050 0014510 g ustar 00root root 0000000 0000000 52 comment=abae165d333d1f653cd2cc4b3f3cf3fdc35c6fbb
EasyProcess-1.1/ 0000775 0000000 0000000 00000000000 14170524050 0013611 5 ustar 00root root 0000000 0000000 EasyProcess-1.1/.github/ 0000775 0000000 0000000 00000000000 14170524050 0015151 5 ustar 00root root 0000000 0000000 EasyProcess-1.1/.github/workflows/ 0000775 0000000 0000000 00000000000 14170524050 0017206 5 ustar 00root root 0000000 0000000 EasyProcess-1.1/.github/workflows/main.yml 0000664 0000000 0000000 00000003500 14170524050 0020653 0 ustar 00root root 0000000 0000000 # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: Python package
on:
schedule:
# * is a special character in YAML so you have to quote this string
- cron: '30 5 1 * *'
push:
pull_request:
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- "ubuntu-18.04"
- "macos-10.15"
- "macos-11"
- "windows-2019"
- "windows-2022"
python-version:
- "3.9"
- "3.10"
stdfile:
- "pipe"
- "tempfile"
include:
- python-version: "3.6"
os: ubuntu-20.04
stdfile: "tempfile"
- python-version: "3.7"
os: ubuntu-20.04
stdfile: "tempfile"
- python-version: "3.8"
os: ubuntu-20.04
stdfile: "tempfile"
- python-version: "3.9"
os: ubuntu-20.04
stdfile: "tempfile"
- python-version: "3.10"
os: ubuntu-20.04
stdfile: "tempfile"
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install .
pip install -r requirements-test.txt
- name: Test with pytest (tempfiles)
if: matrix.stdfile == 'tempfile'
run: |
cd tests
pytest -v .
- name: Test with pytest (pipes)
if: matrix.stdfile == 'pipe'
run: |
cd tests
pytest -v .
env:
EASYPROCESS_USE_TEMP_FILES: 0
- name: Lint
if: matrix.os == 'ubuntu-20.04'
run: |
./lint.sh
EasyProcess-1.1/.gitignore 0000664 0000000 0000000 00000000476 14170524050 0015610 0 ustar 00root root 0000000 0000000 *.py[c|o]
*.egg
*.egg-info
/build
/dist
/nbproject/
pip-log.txt
/.project
/.pydevproject
/include
/lib
/lib64
/bin
/virtualenv
*.bak
*/build/*
*/_build/*
*/_build/latex/*
*.class
*.png
.version
nosetests.xml
.*
!.git*
!.travis*
!.coveragerc
/distribute_setup.py
sloccount.sc
*.prefs
MANIFEST
*.log
docs/api/*
EasyProcess-1.1/.travis.yml.bak 0000664 0000000 0000000 00000002526 14170524050 0016463 0 ustar 00root root 0000000 0000000 language: python
os: linux
matrix:
include:
- name: 3.7_xenial
python: 3.7
dist: xenial
- name: 3.8_xenial
python: 3.8
dist: xenial
- name: 3.7_bionic
python: 3.7
dist: bionic
- name: 3.8_focal
python: 3.8
dist: focal
- name: 3.9_focal
python: 3.9
dist: focal
- name: "Python 3.7 on macOS"
os: osx
osx_image: xcode11.2 # Python 3.7.4 running on macOS 10.14.4
language: shell # 'language: python' is an error on Travis CI macOS
env: PATH=/Users/travis/Library/Python/3.7/bin:$PATH PIPUSER=--user
- name: "Python 3.8 on Windows"
os: windows # Windows 10.0.17134 N/A Build 17134
language: shell # 'language: python' is an error on Travis CI Windows
before_install:
- choco install python --version 3.8
- python -m pip install --upgrade pip
env: PATH=/c/Python38:/c/Python38/Scripts:$PATH
addons:
apt:
packages:
- xvfb
- imagemagick
install:
- PYTHON=python3
- if [ ${TRAVIS_OS_NAME} == "windows" ]; then PYTHON=python; fi
- $PYTHON -m pip install $PIPUSER --upgrade -r requirements-test.txt
- $PYTHON -m pip install $PIPUSER --upgrade .
script:
- cd tests
- $PYTHON -m pytest -v .
- EASYPROCESS_USE_TEMP_FILES=0 $PYTHON -m pytest -v .
EasyProcess-1.1/LICENSE.txt 0000664 0000000 0000000 00000002435 14170524050 0015440 0 ustar 00root root 0000000 0000000 Copyright (c) 2011, ponty
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
EasyProcess-1.1/MANIFEST.in 0000664 0000000 0000000 00000000056 14170524050 0015350 0 ustar 00root root 0000000 0000000 include LICENSE*
recursive-include tests *.py
EasyProcess-1.1/README.md 0000664 0000000 0000000 00000012107 14170524050 0015071 0 ustar 00root root 0000000 0000000 EasyProcess is an easy to use python subprocess interface.
Links:
* home: https://github.com/ponty/EasyProcess
* PYPI: https://pypi.python.org/pypi/EasyProcess

Features:
- layer on top of [subprocess](https://docs.python.org/library/subprocess.html) module
- easy to start, stop programs
- easy to get standard output/error, return code of programs
- command can be list (preferred) or string (command string is converted to list using shlex.split)
- logging
- timeout
- shell is not supported
- pipes are not supported
- stdout/stderr is set only after the subprocess has finished
- stop() does not kill whole subprocess tree
- unicode support
- supported python versions: 3.7, 3.8, 3.9, 3.10
- [Method chaining](https://en.wikipedia.org/wiki/Method_chaining)
Installation:
```console
$ python3 -m pip install EasyProcess
```
Usage
=====
Examples:
```py
# easyprocess/examples/hello.py
from easyprocess import EasyProcess
cmd = ["echo", "hello"]
s = EasyProcess(cmd).call().stdout
print(s)
```
Output:
```console
$ python3 -m easyprocess.examples.hello
hello
```
```py
# easyprocess/examples/cmd.py
import sys
from easyprocess import EasyProcess
python = sys.executable
print("-- Run program, wait for it to complete, get stdout:")
s = EasyProcess([python, "-c", "print(3)"]).call().stdout
print(s)
print("-- Run program, wait for it to complete, get stderr:")
s = EasyProcess([python, "-c", "import sys;sys.stderr.write('4\\n')"]).call().stderr
print(s)
print("-- Run program, wait for it to complete, get return code:")
s = EasyProcess([python, "--version"]).call().return_code
print(s)
print("-- Run program, wait 1.5 second, stop it, get stdout:")
prog = """
import time
for i in range(10):
print(i, flush=True)
time.sleep(1)
"""
s = EasyProcess([python, "-c", prog]).start().sleep(1.5).stop().stdout
print(s)
```
Output:
```console
$ python3 -m easyprocess.examples.cmd
-- Run program, wait for it to complete, get stdout:
3
-- Run program, wait for it to complete, get stderr:
4
-- Run program, wait for it to complete, get return code:
0
-- Run program, wait 1.5 second, stop it, get stdout:
0
1
```
Shell commands
--------------
Shell commands are not supported.
``echo`` is a shell command on Windows (there is no echo.exe),
but it is a program on Linux.
return_code
-----------
`EasyProcess.return_code` is None until
`EasyProcess.stop` or `EasyProcess.wait` is called.
With
----
By using `with` statement the process is started
and stopped automatically:
```python
from easyprocess import EasyProcess
with EasyProcess(["ping", "127.0.0.1"]) as proc: # start()
# communicate with proc
pass
# stopped
```
Equivalent:
```python
from easyprocess import EasyProcess
proc = EasyProcess(["ping", "127.0.0.1"]).start()
try:
# communicate with proc
pass
finally:
proc.stop()
```
Full example:
```py
# easyprocess/examples/with.py
import os
import sys
import urllib.request
from os.path import abspath, dirname
from time import sleep
from easyprocess import EasyProcess
webserver_code = """
from http.server import HTTPServer, CGIHTTPRequestHandler
srv = HTTPServer(server_address=("", 8080), RequestHandlerClass=CGIHTTPRequestHandler)
srv.serve_forever()
"""
os.chdir(dirname(abspath(__file__)))
with EasyProcess([sys.executable, "-c", webserver_code]):
sleep(2) # wait for server
html = urllib.request.urlopen("http://localhost:8080").read().decode("utf-8")
print(html)
```
Output:
```console
$ python3 -m easyprocess.examples.with
Directory listing for /
Directory listing for /
```
Timeout
-------
```py
# easyprocess/examples/timeout.py
import sys
from easyprocess import EasyProcess
python = sys.executable
prog = """
import time
for i in range(3):
print(i, flush=True)
time.sleep(1)
"""
print("-- no timeout")
stdout = EasyProcess([python, "-c", prog]).call().stdout
print(stdout)
print("-- timeout=1.5s")
stdout = EasyProcess([python, "-c", prog]).call(timeout=1.5).stdout
print(stdout)
print("-- timeout=50s")
stdout = EasyProcess([python, "-c", prog]).call(timeout=50).stdout
print(stdout)
```
Output:
```console
$ python3 -m easyprocess.examples.timeout
-- no timeout
0
1
2
-- timeout=1.5s
0
1
-- timeout=50s
0
1
2
``` EasyProcess-1.1/Vagrantfile 0000664 0000000 0000000 00000000776 14170524050 0016010 0 ustar 00root root 0000000 0000000 Vagrant.configure(2) do |config|
config.vm.box = "ubuntu/focal64"
config.vm.provider "virtualbox" do |vb|
#vb.gui = true
# vb.memory = "2048"
vb.name = "easyprocess_2004"
# https://bugs.launchpad.net/cloud-images/+bug/1829625
# vb.customize ["modifyvm", :id, "--uart1", "0x3F8", "4"]
# vb.customize ["modifyvm", :id, "--uartmode1", "file", "./ttyS0.log"]
end
config.vm.provision "shell", path: "vagrant.sh"
config.ssh.extra_args = ["-t", "cd /vagrant; bash --login"]
end
EasyProcess-1.1/clean.py 0000775 0000000 0000000 00000000725 14170524050 0015254 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python3
import pathlib
import shutil
[shutil.rmtree(p) for p in pathlib.Path(".").glob(".tox")]
[shutil.rmtree(p) for p in pathlib.Path(".").glob("dist")]
[shutil.rmtree(p) for p in pathlib.Path(".").glob("*.egg-info")]
[shutil.rmtree(p) for p in pathlib.Path(".").glob("build")]
[p.unlink() for p in pathlib.Path(".").rglob("*.py[co]")]
[p.rmdir() for p in pathlib.Path(".").rglob("__pycache__")]
[p.unlink() for p in pathlib.Path(".").rglob("*.log")]
EasyProcess-1.1/doc/ 0000775 0000000 0000000 00000000000 14170524050 0014356 5 ustar 00root root 0000000 0000000 EasyProcess-1.1/doc/gen/ 0000775 0000000 0000000 00000000000 14170524050 0015127 5 ustar 00root root 0000000 0000000 EasyProcess-1.1/doc/gen/python3_-m_easyprocess.examples.cmd.txt 0000664 0000000 0000000 00000000411 14170524050 0024660 0 ustar 00root root 0000000 0000000 $ python3 -m easyprocess.examples.cmd
-- Run program, wait for it to complete, get stdout:
3
-- Run program, wait for it to complete, get stderr:
4
-- Run program, wait for it to complete, get return code:
0
-- Run program, wait 1.5 second, stop it, get stdout:
0
1 EasyProcess-1.1/doc/gen/python3_-m_easyprocess.examples.hello.txt 0000664 0000000 0000000 00000000055 14170524050 0025224 0 ustar 00root root 0000000 0000000 $ python3 -m easyprocess.examples.hello
hello EasyProcess-1.1/doc/gen/python3_-m_easyprocess.examples.timeout.txt 0000664 0000000 0000000 00000000146 14170524050 0025610 0 ustar 00root root 0000000 0000000 $ python3 -m easyprocess.examples.timeout
-- no timeout
0
1
2
-- timeout=1.5s
0
1
-- timeout=50s
0
1
2 EasyProcess-1.1/doc/gen/python3_-m_easyprocess.examples.with.txt 0000664 0000000 0000000 00000001234 14170524050 0025074 0 ustar 00root root 0000000 0000000 $ python3 -m easyprocess.examples.with
Directory listing for /
Directory listing for /
EasyProcess-1.1/doc/generate-doc.py 0000664 0000000 0000000 00000002577 14170524050 0017300 0 ustar 00root root 0000000 0000000 import glob
import logging
import os
from entrypoint2 import entrypoint
from easyprocess import EasyProcess
commands = [
"python3 -m easyprocess.examples.hello",
"python3 -m easyprocess.examples.cmd",
"python3 -m easyprocess.examples.timeout",
"python3 -m easyprocess.examples.with",
]
def empty_dir(dir):
files = glob.glob(os.path.join(dir, "*"))
for f in files:
os.remove(f)
@entrypoint
def main():
gendir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "gen")
logging.info("gendir: %s", gendir)
os.makedirs(gendir, exist_ok=True)
empty_dir(gendir)
pls = []
try:
os.chdir("gen")
for cmd in commands:
logging.info("cmd: %s", cmd)
fname_base = cmd.replace(" ", "_")
fname = fname_base + ".txt"
logging.info("cmd: %s", cmd)
print("file name: %s" % fname)
with open(fname, "w") as f:
f.write("$ " + cmd + "\n")
p = EasyProcess(cmd).call()
f.write(p.stdout)
f.write(p.stderr)
pls += [p]
finally:
os.chdir("..")
for p in pls:
p.stop()
embedme = EasyProcess(["npx", "embedme", "../README.md"])
embedme.call()
print(embedme.stdout)
assert embedme.return_code == 0
assert "but file does not exist" not in embedme.stdout
EasyProcess-1.1/easyprocess/ 0000775 0000000 0000000 00000000000 14170524050 0016151 5 ustar 00root root 0000000 0000000 EasyProcess-1.1/easyprocess/__init__.py 0000664 0000000 0000000 00000026603 14170524050 0020271 0 ustar 00root root 0000000 0000000 """Easy to use python subprocess interface."""
import logging
import os.path
import subprocess
import tempfile
import time
from typing import Any, List, Optional, Union
from easyprocess.about import __version__
from easyprocess.unicodeutil import split_command, unidecode
log = logging.getLogger(__name__)
log.debug("version=%s", __version__)
class EasyProcessError(Exception):
def __init__(self, easy_process, msg=""):
self.easy_process = easy_process
self.msg = msg
def __str__(self):
return self.msg + " " + repr(self.easy_process)
def _rm_ending_lf(s):
if s.endswith("\n"):
s = s[:-1]
if s.endswith("\r"):
s = s[:-1]
return s
class EasyProcess(object):
"""
.. module:: easyprocess
simple interface for :mod:`subprocess`
shell is not supported (shell=False)
.. warning::
unicode is supported only for string list command (Python2.x)
(check :mod:`shlex` for more information)
:param cmd: string ('ls -l') or list of strings (['ls','-l'])
:param cwd: working directory
:param use_temp_files: use temp files (True) or pipes (False) for stdout and stderr,
pipes can cause deadlock in some cases (see unit tests)
:param env: If *env* is not ``None``, it must be a mapping that defines the environment
variables for the new process; these are used instead of inheriting the current
process' environment, which is the default behavior.
(check :mod:`subprocess` for more information)
"""
def __init__(
self,
cmd: Union[List[str], str],
cwd: Optional[str] = None,
use_temp_files: bool = True,
env=None,
):
self.use_temp_files = use_temp_files
# for testing
EASYPROCESS_USE_TEMP_FILES = os.environ.get("EASYPROCESS_USE_TEMP_FILES")
if EASYPROCESS_USE_TEMP_FILES:
log.debug("EASYPROCESS_USE_TEMP_FILES=%s", EASYPROCESS_USE_TEMP_FILES)
# '0'->false, '1'->true
self.use_temp_files = bool(int(EASYPROCESS_USE_TEMP_FILES))
self._outputs_processed = False
self.env = env
self.popen: Optional[subprocess.Popen] = None
self.stdout = None
self.stderr = None
self._stdout_file: Any = None
self._stderr_file: Any = None
self.is_started = False
self.oserror: Optional[OSError] = None
self.cmd_param = cmd
# self._thread: Optional[threading.Thread] = None
self.timeout_happened = False
self.cwd = cwd
self.cmd = split_command(cmd)
# self.cmd_as_string = " ".join(self.cmd)
self.enable_stdout_log = True
self.enable_stderr_log = True
# log.debug('param: "%s" ', self.cmd_param)
log.debug("command: %s", self.cmd)
# log.debug('joined command: %s', self.cmd_as_string)
if not len(self.cmd):
raise EasyProcessError(self, "empty command!")
def __repr__(self):
msg = '<%s cmd_param=%s cmd=%s oserror=%s return_code=%s stdout="%s" stderr="%s" timeout_happened=%s>' % (
self.__class__.__name__,
self.cmd_param,
self.cmd,
self.oserror,
self.return_code,
self.stdout,
self.stderr,
self.timeout_happened,
)
return msg
@property
def pid(self) -> Optional[int]:
"""
PID (:attr:`subprocess.Popen.pid`)
:rtype: int
"""
if self.popen:
return self.popen.pid
return None
@property
def return_code(self) -> Optional[int]:
"""
returncode (:attr:`subprocess.Popen.returncode`)
:rtype: int
"""
if self.popen:
return self.popen.returncode
return None
def call(self, timeout: Optional[float] = None) -> "EasyProcess":
"""Run command with arguments. Wait for command to complete.
same as:
1. :meth:`start`
2. :meth:`wait`
3. :meth:`stop`
:rtype: self
"""
try:
self.start().wait(timeout=timeout)
finally:
if self.is_alive():
self.stop()
return self
def start(self) -> "EasyProcess":
"""start command in background and does not wait for it.
:rtype: self
"""
if self.is_started:
raise EasyProcessError(self, "process was started twice!")
stdout: Any = None
stderr: Any = None
if self.use_temp_files:
self._stdout_file = tempfile.TemporaryFile(prefix="stdout_")
self._stderr_file = tempfile.TemporaryFile(prefix="stderr_")
stdout = self._stdout_file
stderr = self._stderr_file
else:
stdout = subprocess.PIPE
stderr = subprocess.PIPE
# cmd = list(map(uniencode, self.cmd))
try:
self.popen = subprocess.Popen(
self.cmd,
stdout=stdout,
stderr=stderr,
cwd=self.cwd,
env=self.env,
)
except OSError as oserror:
log.debug("OSError exception: %s", oserror)
self.oserror = oserror
raise EasyProcessError(self, "start error")
self.is_started = True
log.debug("process was started (pid=%s)", self.pid)
return self
def is_alive(self) -> bool:
"""
poll process using :meth:`subprocess.Popen.poll`
It updates stdout/stderr/return_code if process has stopped earlier.
:rtype: bool
"""
if self.popen:
alive = self.popen.poll() is None
if not alive:
# collect stdout/stderr/return_code if proc stopped
self._wait4process()
return alive
else:
return False
def wait(self, timeout: Optional[float] = None) -> "EasyProcess":
"""Wait for command to complete.
:rtype: self
"""
# Timeout (threading) discussion: https://stackoverflow.com/questions/1191374/subprocess-with-timeout
self._wait4process(timeout)
# if timeout is not None:
# if not self._thread:
# self._thread = threading.Thread(target=self._wait4process)
# self._thread.daemon = True
# self._thread.start()
# if self._thread:
# self._thread.join(timeout=timeout)
# self.timeout_happened = self.timeout_happened or self._thread.is_alive()
# else:
# # no timeout and no existing thread
# self._wait4process()
return self
def _wait4process(self, timeout=None):
if self._outputs_processed:
return
if not self.popen:
return
if self.use_temp_files:
try:
self.popen.wait(timeout=timeout)
except subprocess.TimeoutExpired:
self.timeout_happened = True
log.debug("timeout")
return
self._stdout_file.seek(0)
self._stderr_file.seek(0)
self.stdout = self._stdout_file.read()
self.stderr = self._stderr_file.read()
self._stdout_file.close()
self._stderr_file.close()
else:
# This will deadlock when using stdout=PIPE and/or stderr=PIPE
# and the child process generates enough output to a pipe such
# that it blocks waiting for the OS pipe buffer to accept more data.
# Use communicate() to avoid that.
# self.popen.wait()
# self.stdout = self.popen.stdout.read()
# self.stderr = self.popen.stderr.read()
try:
(self.stdout, self.stderr) = self.popen.communicate(timeout=timeout)
except subprocess.TimeoutExpired:
self.timeout_happened = True
log.debug("timeout")
return
log.debug("process has ended, return code=%s", self.return_code)
self.stdout = _rm_ending_lf(unidecode(self.stdout))
self.stderr = _rm_ending_lf(unidecode(self.stderr))
self._outputs_processed = True
# def limit_str(s):
# if len(s) > self.max_bytes_to_log:
# warn = '[middle of output was removed, max_bytes_to_log=%s]'%(self.max_bytes_to_log)
# s = s[:self.max_bytes_to_log / 2] + warn + s[-self.max_bytes_to_log / 2:]
# return s
if self.enable_stdout_log:
log.debug("stdout=%s", self.stdout)
if self.enable_stderr_log:
log.debug("stderr=%s", self.stderr)
def stop(self) -> "EasyProcess":
"""Kill process and wait for command to complete.
same as:
1. :meth:`sendstop`
2. :meth:`wait`
:rtype: self
"""
self.sendstop().wait()
# if self.is_alive() and kill_after is not None:
# self.sendstop(kill=True).wait()
return self
def sendstop(self) -> "EasyProcess":
"""
Kill process (:meth:`subprocess.Popen.terminate`).
Do not wait for command to complete.
:rtype: self
"""
if not self.is_started:
raise EasyProcessError(self, "process was not started!")
log.debug('stopping process (pid=%s cmd="%s")', self.pid, self.cmd)
if self.popen:
if self.is_alive():
log.debug("process is active -> calling kill()")
self.popen.kill()
# signame = "SIGKILL" if kill else "SIGTERM"
# log.debug("process is active -> sending " + signame)
# try:
# try:
# if kill:
# self.popen.kill()
# else:
# self.popen.terminate()
# except AttributeError:
# os.kill(self.popen.pid, signal.SIGKILL)
# except OSError as oserror:
# log.debug("exception in terminate:%s", oserror)
else:
log.debug("process was already stopped")
else:
log.debug("process was not started")
return self
def sleep(self, sec: float) -> "EasyProcess":
"""
sleeping (same as :func:`time.sleep`)
:rtype: self
"""
time.sleep(sec)
return self
def wrap(self, func, delay=0):
"""
returns a function which:
1. start process
2. call func, save result
3. stop process
4. returns result
similar to :keyword:`with` statement
:rtype:
"""
def wrapped():
self.start()
if delay:
self.sleep(delay)
x = None
try:
x = func()
except OSError as oserror:
log.debug("OSError exception:%s", oserror)
self.oserror = oserror
raise EasyProcessError(self, "wrap error!")
finally:
self.stop()
return x
return wrapped
def __enter__(self):
"""used by the :keyword:`with` statement"""
self.start()
return self
def __exit__(self, *exc_info):
"""used by the :keyword:`with` statement"""
self.stop()
EasyProcess-1.1/easyprocess/about.py 0000664 0000000 0000000 00000000024 14170524050 0017631 0 ustar 00root root 0000000 0000000 __version__ = "1.1"
EasyProcess-1.1/easyprocess/examples/ 0000775 0000000 0000000 00000000000 14170524050 0017767 5 ustar 00root root 0000000 0000000 EasyProcess-1.1/easyprocess/examples/__init__.py 0000664 0000000 0000000 00000000000 14170524050 0022066 0 ustar 00root root 0000000 0000000 EasyProcess-1.1/easyprocess/examples/cmd.py 0000664 0000000 0000000 00000001331 14170524050 0021102 0 ustar 00root root 0000000 0000000 import sys
from easyprocess import EasyProcess
python = sys.executable
print("-- Run program, wait for it to complete, get stdout:")
s = EasyProcess([python, "-c", "print(3)"]).call().stdout
print(s)
print("-- Run program, wait for it to complete, get stderr:")
s = EasyProcess([python, "-c", "import sys;sys.stderr.write('4\\n')"]).call().stderr
print(s)
print("-- Run program, wait for it to complete, get return code:")
s = EasyProcess([python, "--version"]).call().return_code
print(s)
print("-- Run program, wait 1.5 second, stop it, get stdout:")
prog = """
import time
for i in range(10):
print(i, flush=True)
time.sleep(1)
"""
s = EasyProcess([python, "-c", prog]).start().sleep(1.5).stop().stdout
print(s)
EasyProcess-1.1/easyprocess/examples/hello.py 0000664 0000000 0000000 00000000151 14170524050 0021441 0 ustar 00root root 0000000 0000000 from easyprocess import EasyProcess
cmd = ["echo", "hello"]
s = EasyProcess(cmd).call().stdout
print(s)
EasyProcess-1.1/easyprocess/examples/log.py 0000664 0000000 0000000 00000000372 14170524050 0021124 0 ustar 00root root 0000000 0000000 import logging
import sys
from easyprocess import EasyProcess
python = sys.executable
# turn on logging
logging.basicConfig(level=logging.DEBUG)
EasyProcess([python, "--version"]).call()
EasyProcess(["ping", "localhost"]).start().sleep(1).stop()
EasyProcess-1.1/easyprocess/examples/timeout.py 0000664 0000000 0000000 00000000730 14170524050 0022027 0 ustar 00root root 0000000 0000000 import sys
from easyprocess import EasyProcess
python = sys.executable
prog = """
import time
for i in range(3):
print(i, flush=True)
time.sleep(1)
"""
print("-- no timeout")
stdout = EasyProcess([python, "-c", prog]).call().stdout
print(stdout)
print("-- timeout=1.5s")
stdout = EasyProcess([python, "-c", prog]).call(timeout=1.5).stdout
print(stdout)
print("-- timeout=50s")
stdout = EasyProcess([python, "-c", prog]).call(timeout=50).stdout
print(stdout)
EasyProcess-1.1/easyprocess/examples/ver.py 0000664 0000000 0000000 00000000242 14170524050 0021133 0 ustar 00root root 0000000 0000000 import sys
from easyprocess import EasyProcess
python = sys.executable
v = EasyProcess([python, "--version"]).call().stderr
print("your python version:%s" % v)
EasyProcess-1.1/easyprocess/examples/with.py 0000664 0000000 0000000 00000001050 14170524050 0021310 0 ustar 00root root 0000000 0000000 import os
import sys
import urllib.request
from os.path import abspath, dirname
from time import sleep
from easyprocess import EasyProcess
webserver_code = """
from http.server import HTTPServer, CGIHTTPRequestHandler
srv = HTTPServer(server_address=("", 8080), RequestHandlerClass=CGIHTTPRequestHandler)
srv.serve_forever()
"""
os.chdir(dirname(abspath(__file__)))
with EasyProcess([sys.executable, "-c", webserver_code]):
sleep(2) # wait for server
html = urllib.request.urlopen("http://localhost:8080").read().decode("utf-8")
print(html)
EasyProcess-1.1/easyprocess/py.typed 0000664 0000000 0000000 00000000000 14170524050 0017636 0 ustar 00root root 0000000 0000000 EasyProcess-1.1/easyprocess/unicodeutil.py 0000664 0000000 0000000 00000001257 14170524050 0021054 0 ustar 00root root 0000000 0000000 import logging
import shlex
from typing import List
log = logging.getLogger(__name__)
class EasyProcessUnicodeError(Exception):
pass
def split_command(cmd, posix=None) -> List[str]:
"""
- cmd is string list -> nothing to do
- cmd is string -> split it using shlex
:param cmd: string ('ls -l') or list of strings (['ls','-l'])
:rtype: string list
"""
if not isinstance(cmd, str):
# cmd is string list
pass
else:
if posix is None:
posix = True
cmd = shlex.split(cmd, posix=posix)
return cmd
# def uniencode(s):
# return s
def unidecode(s):
s = s.decode("utf-8", "ignore")
return s
EasyProcess-1.1/format-code.sh 0000775 0000000 0000000 00000000177 14170524050 0016355 0 ustar 00root root 0000000 0000000 #!/bin/bash
set -e
autoflake -i -r --remove-all-unused-imports .
autoflake -i -r --remove-unused-variables .
isort .
black .
EasyProcess-1.1/lint.sh 0000775 0000000 0000000 00000000161 14170524050 0015114 0 ustar 00root root 0000000 0000000 #!/bin/bash
set -e
python3 -m flake8 . --max-complexity=10 --max-line-length=127
python3 -m mypy "easyprocess"
EasyProcess-1.1/pytest.ini 0000664 0000000 0000000 00000000216 14170524050 0015641 0 ustar 00root root 0000000 0000000 [pytest]
log_level=DEBUG
log_format=%(asctime)s.%(msecs)03d %(name)s %(levelname)s %(message)s
log_date_format=%Y-%m-%d %H:%M:%S
#log_cli=true EasyProcess-1.1/requirements-doc.txt 0000664 0000000 0000000 00000000145 14170524050 0017640 0 ustar 00root root 0000000 0000000 autoflake
isort
black>=21.12b0
# pytest
# pytest-timeout
pyvirtualdisplay
entrypoint2
pillow
# mypy
EasyProcess-1.1/requirements-test.txt 0000664 0000000 0000000 00000000106 14170524050 0020047 0 ustar 00root root 0000000 0000000 pytest
pytest-timeout
pyvirtualdisplay
entrypoint2
pillow
mypy
flake8
EasyProcess-1.1/setup.py 0000664 0000000 0000000 00000002633 14170524050 0015327 0 ustar 00root root 0000000 0000000 import os
from setuptools import setup
NAME = "easyprocess"
# get __version__
__version__ = None
exec(open(os.path.join(NAME, "about.py")).read())
VERSION = __version__
PYPI_NAME = "EasyProcess"
URL = "https://github.com/ponty/easyprocess"
DESCRIPTION = "Easy to use Python subprocess interface."
LONG_DESCRIPTION = """Easy to use Python subprocess interface.
Documentation: https://github.com/ponty/easyprocess/tree/"""
LONG_DESCRIPTION += VERSION
PACKAGES = [
NAME,
NAME + ".examples",
]
classifiers = [
# Get more strings from
# https://pypi.python.org/pypi?%3Aaction=list_classifiers
"License :: OSI Approved :: BSD License",
"Natural Language :: English",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
]
setup(
name=PYPI_NAME,
version=VERSION,
description=DESCRIPTION,
long_description=LONG_DESCRIPTION,
long_description_content_type="text/x-rst",
classifiers=classifiers,
keywords="subprocess interface",
author="ponty",
# author_email='',
url=URL,
license="BSD",
packages=PACKAGES,
package_data={
NAME: ["py.typed"],
},
)
EasyProcess-1.1/tests/ 0000775 0000000 0000000 00000000000 14170524050 0014753 5 ustar 00root root 0000000 0000000 EasyProcess-1.1/tests/test_fast/ 0000775 0000000 0000000 00000000000 14170524050 0016747 5 ustar 00root root 0000000 0000000 EasyProcess-1.1/tests/test_fast/test_deadlock.py 0000664 0000000 0000000 00000003241 14170524050 0022126 0 ustar 00root root 0000000 0000000 import os
import sys
import threading
from time import sleep
import pytest
from pyvirtualdisplay.display import Display
from easyprocess import EasyProcess
python = sys.executable
# requirement: apt install imagemagick
# deadlock
# popen.communicate() hangs
# no deadlock with temp_files
PROG = """
from PIL import Image
Image.new("RGB",(99, 99)).show()
"""
EASYPROCESS_USE_TEMP_FILES = os.environ.get("EASYPROCESS_USE_TEMP_FILES")
def test_dummy():
pass
# skip these tests for Windows/Mac
# and when 'use_temp_files' is forced by env variable
if sys.platform.startswith("linux") and not EASYPROCESS_USE_TEMP_FILES:
def test_has_imagemagick():
assert EasyProcess(["display", "-version"]).call().return_code == 0
@pytest.mark.timeout(120)
def test_deadlock_temp_files():
with Display():
p = EasyProcess(
[
python,
"-c",
PROG,
],
use_temp_files=True,
)
p.start()
sleep(2)
# hangs with pipes
p.stop()
@pytest.mark.timeout(120)
def test_deadlock_pipe():
with Display():
p = EasyProcess(
[
python,
"-c",
PROG,
],
use_temp_files=False,
)
p.start()
sleep(2)
def start():
# hangs with pipes
p.stop()
thread = threading.Thread(target=start)
thread.start()
sleep(6)
assert thread.is_alive()
thread.join()
EasyProcess-1.1/tests/test_fast/test_env.py 0000664 0000000 0000000 00000000662 14170524050 0021154 0 ustar 00root root 0000000 0000000 import json
import sys
from easyprocess import EasyProcess
python = sys.executable
def pass_env(e):
prog = "import os,json;print(json.dumps(dict(os.environ)))"
s = EasyProcess([python, "-c", prog], env=e).call().stdout
return json.loads(s)
def test_env():
assert len(pass_env(None)) > 0
e = pass_env(None)
assert pass_env(e).get("FOO") is None
e["FOO"] = "2"
assert pass_env(e).get("FOO") == "2"
EasyProcess-1.1/tests/test_fast/test_examples.py 0000664 0000000 0000000 00000001551 14170524050 0022200 0 ustar 00root root 0000000 0000000 import sys
from easyprocess import EasyProcess
def test():
# skip these tests for Windows/Mac
if not sys.platform.startswith("linux"):
return
assert (
EasyProcess([sys.executable, "-m", "easyprocess.examples.ver"])
.call()
.return_code
== 0
)
assert (
EasyProcess([sys.executable, "-m", "easyprocess.examples.log"])
.call()
.return_code
== 0
)
assert (
EasyProcess([sys.executable, "-m", "easyprocess.examples.cmd"])
.call()
.return_code
== 0
)
assert (
EasyProcess([sys.executable, "-m", "easyprocess.examples.hello"])
.call()
.return_code
== 0
)
assert (
EasyProcess([sys.executable, "-m", "easyprocess.examples.timeout"])
.call()
.return_code
== 0
)
EasyProcess-1.1/tests/test_fast/test_proc.py 0000664 0000000 0000000 00000004202 14170524050 0021321 0 ustar 00root root 0000000 0000000 from __future__ import with_statement
import sys
import time
import pytest
from easyprocess import EasyProcess
python = sys.executable
def test_call():
assert EasyProcess("ls -la").call().return_code == 0
assert EasyProcess(["ls", "-la"]).call().return_code == 0
def test_start():
p = EasyProcess("ls -la").start()
time.sleep(0.2)
assert p.stop().return_code == 0
def test_start2():
p = EasyProcess("echo hi").start()
time.sleep(0.2)
# no wait() -> no results
assert p.return_code is None
assert p.stdout is None
@pytest.mark.timeout(1)
def test_start3():
p = EasyProcess("sleep 10").start()
assert p.return_code is None
def test_alive():
assert EasyProcess("ping 127.0.0.1 -c 2").is_alive() is False
assert EasyProcess("ping 127.0.0.1 -c 2").start().is_alive()
assert EasyProcess("ping 127.0.0.1 -c 2").start().stop().is_alive() is False
assert EasyProcess("ping 127.0.0.1 -c 2").call().is_alive() is False
def test_std():
assert EasyProcess("echo hello").call().stdout == "hello"
assert EasyProcess([python, "-c", "print(42)"]).call().stdout == "42"
def test_wait():
assert EasyProcess("echo hello").wait().return_code is None
assert EasyProcess("echo hello").wait().stdout is None
assert EasyProcess("echo hello").start().wait().return_code == 0
assert EasyProcess("echo hello").start().wait().stdout == "hello"
# def test_xephyr():
# EasyProcess('Xephyr -help').check(return_code=1)
def test_wrap():
def f():
return EasyProcess("echo hi").call().stdout
assert EasyProcess("ping 127.0.0.1").wrap(f)() == "hi"
def test_with():
with EasyProcess("ping 127.0.0.1") as x:
assert x.is_alive()
assert x.return_code != 0
assert not x.is_alive()
def test_parse():
assert EasyProcess("ls -la").cmd == ["ls", "-la"]
assert EasyProcess('ls "abc"').cmd == ["ls", "abc"]
assert EasyProcess('ls "ab c"').cmd == ["ls", "ab c"]
def test_stop():
p = EasyProcess("ls -la").start()
time.sleep(0.2)
assert p.stop().return_code == 0
assert p.stop().return_code == 0
assert p.stop().return_code == 0
EasyProcess-1.1/tests/test_fast/test_returncode.py 0000664 0000000 0000000 00000002407 14170524050 0022535 0 ustar 00root root 0000000 0000000 from easyprocess import EasyProcess
def test_return_code():
# process has finished but no stop() or wait() was called
assert EasyProcess("echo hello").start().sleep(0.5).return_code is None
# wait()
assert EasyProcess("echo hello").start().wait().return_code == 0
# stop() after process has finished
assert EasyProcess("echo hello").start().sleep(0.5).stop().return_code == 0
# stop() before process has finished
assert EasyProcess("sleep 2").start().stop().return_code != 0
# same as start().wait().stop()
assert EasyProcess("echo hello").call().return_code == 0
def test_is_alive1():
# early exit
p = EasyProcess("echo hello").start().sleep(0.5)
assert p.return_code is None
assert p.stdout is None
assert p.stderr is None
assert p.is_alive() is False # is_alive collects ouputs if proc stopped
assert p.return_code == 0
assert p.stdout == "hello"
assert p.stderr == ""
def test_is_alive2():
# no exit
p = EasyProcess("sleep 10").start()
assert p.return_code is None
assert p.stdout is None
assert p.stderr is None
assert p.is_alive() # is_alive collects ouputs if proc stopped
assert p.return_code is None
assert p.stdout is None
assert p.stderr is None
EasyProcess-1.1/tests/test_fast/test_started.py 0000664 0000000 0000000 00000001564 14170524050 0022034 0 ustar 00root root 0000000 0000000 import pytest
from easyprocess import EasyProcess, EasyProcessError
def test_is_started():
assert EasyProcess("ls -la").is_started is False
assert EasyProcess("ls -la").start().is_started
assert EasyProcess("ls -la").call().is_started
assert EasyProcess("ls -la").start().wait().is_started
assert EasyProcess("ls -la").start().stop().is_started
def test_raise():
with pytest.raises(EasyProcessError):
EasyProcess("ls -la").start().start()
with pytest.raises(EasyProcessError):
EasyProcess("ls -la").stop()
with pytest.raises(EasyProcessError):
EasyProcess("ls -la").sendstop()
# .assertRaises(EasyProcessError, lambda : EasyProcess('ls
# -la').start().stop().stop())
with pytest.raises(EasyProcessError):
EasyProcess("ls -la").start().wrap(lambda: None)()
EasyProcess("ls -la").wrap(lambda: None)()
EasyProcess-1.1/tests/test_fast/test_timeout.py 0000664 0000000 0000000 00000010363 14170524050 0022051 0 ustar 00root root 0000000 0000000 import sys
import time
import pytest
from easyprocess import EasyProcess
python = sys.executable
def test_timeout():
p = EasyProcess("sleep 1").start()
p.wait(0.2)
assert p.is_alive()
p.wait(0.2)
assert p.is_alive()
p.wait(2)
assert not p.is_alive()
assert EasyProcess("sleep 0.3").call().return_code == 0
assert EasyProcess("sleep 0.3").call(timeout=0.1).return_code != 0
assert EasyProcess("sleep 0.3").call(timeout=1).return_code == 0
assert EasyProcess("sleep 0.3").call().timeout_happened is False
assert EasyProcess("sleep 0.3").call(timeout=0.1).timeout_happened
assert EasyProcess("sleep 0.3").call(timeout=1).timeout_happened is False
@pytest.mark.timeout(10)
def test_time_cli1():
hdr = "import logging;logging.basicConfig(level=logging.DEBUG);from easyprocess import EasyProcess;"
p = EasyProcess(
[
python,
"-c",
hdr + "EasyProcess('sleep 15').start()",
]
)
p.call()
assert p.return_code == 0
@pytest.mark.timeout(10)
def test_time_cli2():
hdr = "import logging;logging.basicConfig(level=logging.DEBUG);from easyprocess import EasyProcess;"
p = EasyProcess(
[
python,
"-c",
hdr + "EasyProcess('sleep 15').call(timeout=0.5)",
]
)
p.call()
assert p.return_code == 0
@pytest.mark.timeout(10)
def test_time2():
p = EasyProcess("sleep 15").call(timeout=1)
assert p.is_alive() is False
assert p.timeout_happened
assert p.return_code != 0
assert p.stdout == ""
@pytest.mark.timeout(10)
def test_timeout_out():
p = EasyProcess(
[python, "-c", "import time;print( 'start');time.sleep(15);print( 'end')"]
).call(timeout=1)
assert p.is_alive() is False
assert p.timeout_happened
assert p.return_code != 0
assert p.stdout == ""
@pytest.mark.timeout(3)
def test_time3():
EasyProcess("sleep 15").start()
ignore_term = """
import signal;
import time;
signal.signal(signal.SIGTERM, lambda *args: None);
while True:
time.sleep(0.5);
"""
# @pytest.mark.timeout(10)
# def test_force_timeout():
# proc = EasyProcess([python, "-c", ignore_term]).start()
# # Calling stop() right away actually stops python before it
# # has a change to actually compile and run the input code,
# # meaning the signal handlers aren't registered yet. Give it
# # a moment to setup
# time.sleep(1)
# proc.stop(kill_after=1)
# assert proc.is_alive() is False
# assert proc.return_code != 0
@pytest.mark.timeout(30)
def test_kill():
proc = EasyProcess([python, "-c", ignore_term]).start()
# Calling stop() right away actually stops python before it
# has a change to actually compile and run the input code,
# meaning the signal handlers aren't registered yet. Give it
# a moment to setup
time.sleep(3)
proc.stop()
assert proc.is_alive() is False
assert proc.return_code != 0
# @pytest.mark.timeout(10)
# def test_force_0_timeout():
# proc = EasyProcess([python, "-c", ignore_term]).start()
# time.sleep(1)
# proc.stop(kill_after=0)
# assert proc.is_alive() is False
# assert proc.return_code != 0
@pytest.mark.timeout(10)
def test_force_timeout2():
proc = EasyProcess([python, "-c", ignore_term]).call(timeout=1)
assert proc.is_alive() is False
assert proc.return_code != 0
# @pytest.mark.timeout(10)
# def test_stop_wait():
# proc = EasyProcess([python, "-c", ignore_term]).start()
# time.sleep(1)
# proc.sendstop().wait(timeout=1)
# # On windows, Popen.terminate actually behaves like kill,
# # so don't check that our hanging process code is actually hanging.
# # The end result is still what we want. On other platforms, leave
# # this assertion to make sure we are correctly testing the ability
# # to stop a hung process
# if not sys.platform.startswith("win"):
# assert proc.is_alive() is True
# proc.stop(kill_after=1)
# assert proc.is_alive() is False
# assert proc.return_code != 0
@pytest.mark.timeout(30)
def test_stop_wait():
proc = EasyProcess([python, "-c", ignore_term]).start()
time.sleep(3)
proc.sendstop().wait(timeout=3)
assert proc.is_alive() is False
assert proc.return_code != 0
EasyProcess-1.1/tests/test_fast/test_unicode.py 0000664 0000000 0000000 00000006264 14170524050 0022016 0 ustar 00root root 0000000 0000000 import sys
from easyprocess import EasyProcess
from easyprocess.unicodeutil import split_command
OMEGA = "\u03A9"
python = sys.executable
def platform_is_win():
return sys.platform == "win32"
def py_minor():
return sys.version_info[1]
def test_str():
assert EasyProcess("ls -la").call().return_code == 0
def test_ls():
assert EasyProcess(["ls", "-la"]).call().return_code == 0
def test_parse():
assert EasyProcess("ls -la").cmd == ["ls", "-la"]
assert EasyProcess('ls "abc"').cmd == ["ls", "abc"]
assert EasyProcess('ls "ab c"').cmd == ["ls", "ab c"]
def test_split():
# list -> list
assert split_command([str("x"), str("y")]) == ["x", "y"]
assert split_command([str("x"), "y"]) == ["x", "y"]
assert split_command([str("x"), OMEGA]) == ["x", OMEGA]
# str -> list
assert split_command(str("x y")) == ["x", "y"]
assert split_command("x y") == ["x", "y"]
assert split_command("x " + OMEGA) == ["x", OMEGA]
# split windows paths #12
assert split_command("c:\\temp\\a.exe someArg", posix=False) == [
"c:\\temp\\a.exe",
"someArg",
]
def test_echo():
assert EasyProcess("echo hi").call().stdout == "hi"
if not platform_is_win():
assert EasyProcess("echo " + OMEGA).call().stdout == OMEGA
assert EasyProcess(["echo", OMEGA]).call().stdout == OMEGA
def test_argv():
assert (
EasyProcess([python, "-c", r"import sys;assert sys.argv[1]=='123'", "123"])
.call()
.return_code
== 0
)
assert (
EasyProcess([python, "-c", r"import sys;assert sys.argv[1]=='\u03a9'", OMEGA])
.call()
.return_code
== 0
)
if py_minor() > 6: # sys.stdout.reconfigure from py3.7
def test_py_print():
assert (
EasyProcess(
[
python,
"-c",
r"import sys;sys.stdout.reconfigure(encoding='utf-8');print('\u03a9')",
]
)
.call()
.stdout
== OMEGA
)
assert (
EasyProcess(
[
python,
"-c",
r"import sys;sys.stdout.reconfigure(encoding='utf-8');print(sys.argv[1])",
OMEGA,
]
)
.call()
.stdout
== OMEGA
)
def test_py_stdout_write():
assert (
EasyProcess(
[
python,
"-c",
r"import sys;sys.stdout.buffer.write('\u03a9'.encode('utf-8'))",
]
)
.call()
.stdout
== OMEGA
)
def test_invalid_stdout():
"""invalid utf-8 byte in stdout."""
# https://en.wikipedia.org/wiki/UTF-8#Codepage_layout
# 0x92 continuation byte
cmd = [python, "-c", "import sys;sys.stdout.buffer.write(b'\\x92')"]
p = EasyProcess(cmd).call()
assert p.return_code == 0
assert p.stdout == ""
# 0xFF must never appear in a valid UTF-8 sequence
cmd = [python, "-c", "import sys;sys.stdout.buffer.write(b'\\xFF')"]
p = EasyProcess(cmd).call()
assert p.return_code == 0
assert p.stdout == ""
EasyProcess-1.1/tests/test_stress.py 0000664 0000000 0000000 00000000601 14170524050 0017704 0 ustar 00root root 0000000 0000000 from easyprocess import EasyProcess
def test_call():
for x in range(1000):
# test for:
# OSError exception:[Errno 24] Too many open files
print("index=", x)
assert EasyProcess("echo hi").call().return_code == 0
# def test_start(self):
# for x in range(1000):
# print('index=', x)
# EasyProcess('echo hi').start()
EasyProcess-1.1/tests/test_stress2.py 0000664 0000000 0000000 00000000477 14170524050 0020001 0 ustar 00root root 0000000 0000000 import pytest
from easyprocess import EasyProcess
# if run with coverage:
# Fatal Python error: deallocating None
@pytest.mark.timeout(1000)
def test_timeout(): # pragma: no cover
for x in range(1000):
print("index=", x)
assert EasyProcess("sleep 5").call(timeout=0.05).return_code != 0
EasyProcess-1.1/tox.ini 0000664 0000000 0000000 00000001373 14170524050 0015130 0 ustar 00root root 0000000 0000000 [tox]
envlist =
py310
py310-{tempfile,pipe}
py39
py39-{tempfile,pipe}
py38
py37
py36
py3-doc
py3-lint
# Workaround for Vagrant
#toxworkdir={toxinidir}/.tox # default
toxworkdir={homedir}/.tox/easyprocess
[testenv]
deps = -rrequirements-test.txt
setenv =
pipe: EASYPROCESS_USE_TEMP_FILES = 0
tempfile: DUMMY = 1
changedir=tests
commands=
{envpython} -m pytest -v .
[testenv:py3-doc]
allowlist_externals=bash
changedir=doc
deps =
-rrequirements-doc.txt
commands=
bash -c "cd ..;./format-code.sh"
{envpython} generate-doc.py --debug
[testenv:py3-lint]
allowlist_externals=bash
changedir=.
deps = -rrequirements-test.txt
commands=
bash -c "./lint.sh"
EasyProcess-1.1/vagrant.sh 0000664 0000000 0000000 00000001421 14170524050 0015605 0 ustar 00root root 0000000 0000000 #!/bin/bash
export DEBIAN_FRONTEND=noninteractive
sudo update-locale LANG=en_US.UTF-8 LANGUAGE=en.UTF-8
# echo 'export export LC_ALL=C' >> /home/vagrant/.profile
# install python versions
sudo add-apt-repository --yes ppa:deadsnakes/ppa
sudo apt-get update
sudo apt-get install -y python3.6-dev
sudo apt-get install -y python3.7-dev
sudo apt-get install -y python3.8-dev
sudo apt-get install -y python3-distutils
sudo apt-get install -y python3.9-dev
sudo apt-get install -y python3.9-distutils
sudo apt-get install -y python3.10-dev
sudo apt-get install -y python3.10-distutils
# tools
sudo apt-get install -y mc python3-pip xvfb
# test dependencies
sudo pip3 install tox
sudo apt-get install -y imagemagick
# doc dependencies
sudo apt-get install -y npm
sudo npm install -g npx