You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

envdir

Package Overview
Dependencies
Maintainers
3
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

envdir - pypi Package Compare versions

Comparing version
0.6.1
to
0.7
+6
-4
AUTHORS.rst
Authors
=======
George Yoshida
Horst Gutmann
Jannis Leidel
Kuba Janoszek
- Daniel Hahler
- George Yoshida
- Horst Gutmann
- Jannis Leidel
- Kuba Janoszek
- Nicolas Delaby
Changelog
---------
0.7 (08/10/2014)
^^^^^^^^^^^^^^^^
* Use `exec` (`os.execvpe`) to replace the envdir process with the child
process (fixes #20).
* Change `isenvvar()` to only check for `=` in var names.
0.6.1 (12/23/2013)

@@ -5,0 +13,0 @@ ^^^^^^^^^^^^^^^^^^

+1
-1

@@ -55,3 +55,3 @@ # -*- coding: utf-8 -*-

# The short X.Y version.
version = '0.6.1'
version = '0.6'
# The full version, including alpha/beta/rc tags.

@@ -58,0 +58,0 @@ release = '0.6.1'

@@ -39,16 +39,22 @@ Installation

Next step is downloading the actual standalone script. On Windows this entails
using your web browser to download the following URL::
using your web browser to download the following URL:
https://github.com/jezdez/envdir/releases/download/0.6/envdir-0.6.pyz
.. parsed-literal::
\https://github.com/jezdez/envdir/releases/download/|release|/envdir-|release|.pyz
Or simply run this on the command line to trigger the download with your
default web browser::
default web browser:
C:\Windows\Explorer.exe https://github.com/jezdez/envdir/releases/download/0.6/envdir-0.6.pyz
.. parsed-literal::
C:\\Windows\Explorer.exe \https://github.com/jezdez/envdir/releases/download/|release|/envdir-|release|.pyz
Then -- from the location you downloaded the file to -- run the envdir script
like you would any other script::
like you would any other script:
C:\Users\jezdez\Desktop>.\envdir-0.6.pyz ..
.. parsed-literal::
C:\\Users\\jezdez\\Desktop>.\\envdir-|release|.pyz ..
Linux, Mac OS, others

@@ -58,11 +64,15 @@ ^^^^^^^^^^^^^^^^^^^^^

On Linux, Mac OS and other platforms with a shell like bash simply download
the standalone file from Github::
the standalone file from Github:
$ curl -O https://github.com/jezdez/envdir/releases/download/0.6/envdir-0.6.pyz
.. parsed-literal::
$ curl -LO \https://github.com/jezdez/envdir/releases/download/|release|/envdir-|release|.pyz
and then run the file like you would do when running the script installed by
the envdir package (see above)::
the envdir package (see above):
$ ./envdir-0.6.pyz ..
.. parsed-literal::
$ ./envdir-|release|.pyz ..
.. _`PEP 441`: http://www.python.org/dev/peps/pep-0441/

@@ -69,0 +79,0 @@ .. _`PEP 397`: http://www.python.org/dev/peps/pep-0397/

Metadata-Version: 1.1
Name: envdir
Version: 0.6.1
Version: 0.7
Summary: A Python port of daemontools' envdir.

@@ -86,2 +86,10 @@ Home-page: http://envdir.readthedocs.org/

0.7 (08/10/2014)
^^^^^^^^^^^^^^^^
* Use `exec` (`os.execvpe`) to replace the envdir process with the child
process (fixes #20).
* Change `isenvvar()` to only check for `=` in var names.
0.6.1 (12/23/2013)

@@ -88,0 +96,0 @@ ^^^^^^^^^^^^^^^^^^

@@ -7,3 +7,2 @@ AUTHORS.rst

tox.ini
docs/.DS_Store
docs/Makefile

@@ -17,3 +16,2 @@ docs/api.rst

docs/usage.rst
docs/_build/.DS_Store
envdir/__init__.py

@@ -20,0 +18,0 @@ envdir/__main__.py

@@ -12,5 +12,3 @@ import glob

root, name = os.path.split(name)
return (name == name.upper() and
not name.startswith('_') and
not '=' in name)
return '=' not in name

@@ -17,0 +15,0 @@

import optparse
import os
import signal
import subprocess

@@ -10,11 +9,3 @@ import sys

# must have shell = True on Windows
is_windows = sys.platform == 'win32'
if is_windows:
params = {'creationflags': subprocess.CREATE_NEW_PROCESS_GROUP}
else:
params = {'preexec_fn': os.setsid}
class Response(Exception):

@@ -34,3 +25,2 @@ def __init__(self, message='', status=0):

self.parser.prog = 'envdir'
signal.signal(signal.SIGTERM, self.terminate)

@@ -50,3 +40,6 @@ def path(self, path):

frame = sys._getframe()
get_parent = lambda frame: frame.f_back
def get_parent(frame):
return frame.f_back
for _ in range(stacklevel):

@@ -68,3 +61,3 @@ frame = get_parent(frame)

if len(args) == 0:
raise Response("%s\nError: incorrect number of arguments" %
raise Response("%s\nError: incorrect number of arguments\n" %
(self.parser.get_usage()), 2)

@@ -81,10 +74,7 @@

try:
subprocess.check_call([shell],
universal_newlines=True,
bufsize=0,
close_fds=not is_windows,
**params)
subprocess.call([shell])
except OSError as err:
if err.errno == 2:
raise Response("Unable to find shell %s" % shell, err.errno)
raise Response("Unable to find shell %s" % shell,
status=err.errno)
else:

@@ -115,33 +105,7 @@ raise Response("An error occurred: %s" % err,

try:
self.process = subprocess.Popen(args,
universal_newlines=True,
bufsize=0,
close_fds=not is_windows,
**params)
self.process.wait()
os.execvpe(args[0], args, os.environ)
except OSError as err:
if err.errno == 2:
raise Response("Unable to find command %s" %
args[0], err.errno)
else:
raise Response(status=err.errno)
except KeyboardInterrupt:
self.terminate()
raise Response(status=self.process.returncode)
raise Response("Unable to run command %s: %s" %
(args[0], err), status=err.errno)
def terminate(self, *args, **kwargs):
# first send mellow signal
self.quit(signal.SIGTERM)
if self.process.poll() is None:
# still running, kill it
self.quit(signal.SIGKILL)
def quit(self, signal):
if self.process.poll() is None:
proc_pgid = os.getpgid(self.process.pid)
if os.getpgrp() == proc_pgid:
# Just kill the proc, don't kill ourselves too
os.kill(self.process.pid, signal)
else:
# Kill the whole process group
os.killpg(proc_pgid, signal)
raise Response()

@@ -0,3 +1,7 @@

import functools
import os
import signal
import subprocess
import threading
import py

@@ -8,3 +12,2 @@ import pytest

from envdir.runner import Response
from envdir.__main__ import go

@@ -30,3 +33,30 @@

original_execvpe = os.execvpe
def mocked_execvpe(monkeypatch, name, args, env, with_timeout=None,
signal_type=signal.SIGINT):
monkeypatch.setattr('os.execvpe', original_execvpe)
try:
process = subprocess.Popen(args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=os.environ.copy())
if with_timeout:
def killer(pid):
os.kill(pid, signal_type)
timer = threading.Timer(with_timeout,
functools.partial(killer, process.pid))
timer.start()
stdout, stderr = process.communicate()
if process.returncode != 0:
raise OSError(process.returncode, stderr)
finally:
monkeypatch.setattr('os.execvpe', functools.partial(mocked_execvpe,
monkeypatch))
def test_usage(run):

@@ -40,4 +70,6 @@ "Testing the usage"

def test_default(run, tmpenvdir):
def test_default(run, tmpenvdir, monkeypatch):
"Default cases."
monkeypatch.setattr(os, 'execvpe', functools.partial(mocked_execvpe,
monkeypatch))
tmpenvdir.join('DEFAULT').write('test')

@@ -65,4 +97,6 @@ with py.test.raises(Response) as response:

def test_reset(run, tmpenvdir):
def test_reset(run, tmpenvdir, monkeypatch):
"Resetting an env var with an empty file"
monkeypatch.setattr(os, 'execvpe', functools.partial(mocked_execvpe,
monkeypatch))
tmpenvdir.join('RESET').write('')

@@ -77,4 +111,6 @@ os.environ['RESET'] = 'test3'

def test_multiline(run, tmpenvdir):
def test_multiline(run, tmpenvdir, monkeypatch):
"Multiline envdir file"
monkeypatch.setattr(os, 'execvpe', functools.partial(mocked_execvpe,
monkeypatch))
tmpenvdir.join('MULTI_LINE').write("""multi

@@ -88,4 +124,32 @@ line

def test_translate_nulls(run, tmpenvdir):
def test_lowercase_var_names(run, tmpenvdir, monkeypatch):
"Lowercase env var name"
monkeypatch.setattr(os, 'execvpe', functools.partial(mocked_execvpe,
monkeypatch))
tmpenvdir.join('lowercase-variable').write("test")
with py.test.raises(Response) as response:
run('envdir', str(tmpenvdir), 'ls')
assert 'lowercase-variable' in os.environ
assert os.environ['lowercase-variable'] == 'test'
assert response.value.status == 0
assert response.value.message == ''
def test_var_names_prefixed_by_underscore(run, tmpenvdir, monkeypatch):
"Underscore prefixed env var name"
monkeypatch.setattr(os, 'execvpe', functools.partial(mocked_execvpe,
monkeypatch))
tmpenvdir.join('_UNDERSCORE_VAR').write("test")
with py.test.raises(Response) as response:
run('envdir', str(tmpenvdir), 'ls')
assert '_UNDERSCORE_VAR' in os.environ
assert os.environ['_UNDERSCORE_VAR'] == 'test'
assert response.value.status == 0
assert response.value.message == ''
def test_translate_nulls(run, tmpenvdir, monkeypatch):
"NULLs are translated into newline"
monkeypatch.setattr(os, 'execvpe', functools.partial(mocked_execvpe,
monkeypatch))
tmpenvdir.join('NULL_CHARS').write("""null\x00character""")

@@ -97,4 +161,6 @@ with py.test.raises(Response):

def test_incorrect_no_args(run, tmpenvdir):
def test_incorrect_no_args(run, tmpenvdir, monkeypatch):
"Incorrect number of arguments"
monkeypatch.setattr(os, 'execvpe', functools.partial(mocked_execvpe,
monkeypatch))
with py.test.raises(Response) as response:

@@ -106,3 +172,5 @@ run('envdir', str(tmpenvdir))

def test_doesnt_exist(run, tmpdir):
def test_doesnt_exist(run, tmpdir, monkeypatch):
monkeypatch.setattr(os, 'execvpe', functools.partial(mocked_execvpe,
monkeypatch))
with py.test.raises(Response) as response:

@@ -115,8 +183,13 @@ run('envdir', str(tmpdir.join('missing')), 'ls')

run('envdir', str(tmpdir), 'doesnt-exist')
assert 'Unable to find command' in response.value.message
result = ('Unable to run command' in response.value.message or
'Unable to find command' in response.value.message)
assert result
assert 2 == response.value.status
def test_must_be_directory(run, tmpdir):
def test_must_be_directory(run, tmpdir, monkeypatch):
"The envdir must be a directory"
monkeypatch.setattr(os, 'execvpe', functools.partial(mocked_execvpe,
monkeypatch))
tmpdir.join('not-a-directory').write('')

@@ -129,3 +202,5 @@ with py.test.raises(Response) as response:

def test_error_code(run, tmpenvdir):
def test_error_code(run, tmpenvdir, monkeypatch):
monkeypatch.setattr(os, 'execvpe', functools.partial(mocked_execvpe,
monkeypatch))
with py.test.raises(Response) as response:

@@ -137,3 +212,5 @@ run('envdir', str(tmpenvdir),

def test_equal_sign(run, tmpenvdir):
def test_equal_sign(run, tmpenvdir, monkeypatch):
monkeypatch.setattr(os, 'execvpe', functools.partial(mocked_execvpe,
monkeypatch))
tmpenvdir.join('EQUAL_SIGN=').write('test')

@@ -148,12 +225,14 @@ with py.test.raises(Response):

@py.test.mark.skipif(timeout is None,
reason="(g)timeout command not found")
def test_keyboard_interrupt(run, tmpenvdir):
with py.test.raises(SystemExit) as exit:
go(run, (str(timeout), '--signal=SIGTERM', '--', '1', 'envdir',
str(tmpenvdir), 'ls'))
if py.std.sys.version_info[:2] == (2, 6):
assert exit.value == 2
else:
assert exit.value.code == 2
def test_keyboard_interrupt(run, tmpenvdir, monkeypatch):
monkeypatch.setattr(os, 'execvpe',
functools.partial(mocked_execvpe,
monkeypatch,
with_timeout=.0000001))
with py.test.raises(Response) as response:
run('envdir', str(tmpenvdir), 'sleep', '1')
# Minus sign is added by subprocess to distinguish signals from exit codes.
# Since we send a signal within the test to stop the process, it is the
# intended behaviour.
# signal.SIGINT is equivalent to KeyboardInterrupt on POSIX.
assert response.value.status == -signal.SIGINT

@@ -160,0 +239,0 @@

@@ -1,1 +0,1 @@

__version__ = '0.6.1' # noqa
__version__ = '0.7' # noqa
Metadata-Version: 1.1
Name: envdir
Version: 0.6.1
Version: 0.7
Summary: A Python port of daemontools' envdir.

@@ -86,2 +86,10 @@ Home-page: http://envdir.readthedocs.org/

0.7 (08/10/2014)
^^^^^^^^^^^^^^^^
* Use `exec` (`os.execvpe`) to replace the envdir process with the child
process (fixes #20).
* Change `isenvvar()` to only check for `=` in var names.
0.6.1 (12/23/2013)

@@ -88,0 +96,0 @@ ^^^^^^^^^^^^^^^^^^

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet