Coder Social home page Coder Social logo

greyside / errand-boy Goto Github PK

View Code? Open in Web Editor NEW
37.0 7.0 6.0 54 KB

A memory-conscious alternative to os.fork() and subprocess.Popen().

Home Page: https://pypi.python.org/pypi/errand-boy/

License: BSD 3-Clause "New" or "Revised" License

Python 100.00%
python linux fork popen pool memory-management unix unix-like

errand-boy's Introduction

errand-boy

https://travis-ci.org/greyside/errand-boy.svg?branch=master https://coveralls.io/repos/greyside/errand-boy/badge.png?branch=master

What does it do?

Uses Python multiprocessing to maintain a pool of worker processes used to execute arbitrary terminal commands.

Why not use subprocess.Popen()?

Under the hood subprocess.Popen() uses os.fork(), which copies the currently running process' memory before launching the command you want to run. If your process uses a lot of memory, such as a Celery worker using an eventlet pool, this can cause a "Cannot allocate memory" error.

errand-boy still uses subprocess.Popen(), but tries to keep a low memory footprint. Your celery greenthread workers can communicate with it via asynchronous network calls.

Further reading:

  1. http://stackoverflow.com/a/13329386/241955
  2. http://stackoverflow.com/a/14942111/241955

Setup

Install:

pip install errand-boy

# optional pip install setproctitle

Usage

Run tests:

cd errand-boy/
python -m unittest discover

Run server:

python -m errand_boy.run

Run client (useful for testing/debugging):

python -m errand_boy.run 'ls -al'

Use the client in your code:

from errand_boy.transports.unixsocket import UNIXSocketTransport


errand_boy_transport = UNIXSocketTransport()

stdout, stderr, returncode = errand_boy_transport.run_cmd('ls -al')

print stdout
print stderr
print returncode

Use a subprocess.Popen-like interface:

from errand_boy.transports.unixsocket import UNIXSocketTransport


errand_boy_transport = UNIXSocketTransport()

# Attribute accesses and function calls on objects retrieved via a session
# result in a call to the errand-boy server, unless that object is a string
# or number type.
with errand_boy_transport.get_session() as session:
    subprocess = session.subprocess

    # Here, subprocess.Popen is actually a reference to the actual objects
    # on the errand-boy server. subprocess.PIPE is an int.
    process = subprocess.Popen('ls -al', stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, close_fds=True)

    # Here, process_stdout and process_stderr are strings and returncode is
    # an int, so their actual values are returned instead of references to
    # the remote objects. This means it's safe to use these values later on
    # outside of the current session.
    process_stdout, process_stderr = process.communicate()
    returncode = process.returncode

print stdout
print stderr
print returncode

# Since the session has been closed, trying this will result in an error:
print process.returncode
# raised errand_boy.exceptions.SessionClosedError()

Run load tests:

python -m errand_boy.run --max-accepts=0

pip install Fabric locustio
cd errand-boy/
fab locust_local

Does it work in other languages?

The client/server use an HTTP-inspired protocol, but the data that's sent back and forth is currently serialized using Python's Pickle format. Support could be added for other serialization types though.

Development

Further reading:

errand-boy's People

Contributors

seanhayes avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

errand-boy's Issues

Programatically start server?

Is there any way for me to start the required server from my module when it is imported? I.e I don't want to have to run python -m errand_boy.run before calling my script, I just want there to be a process started as soon as my package is imported by someone.

Thanks for the help

Server doesn't always receive all the data

Firstly, great tool, thanks!

I'm having an intermittent issue where the server doesn't receive all the data sent by the client. I've added a check that prints out when this happens in my fork: master...snopoke:receive

I've also created a test file: https://gist.github.com/snopoke/34404c5eea460db7f5ac

It doesn't fail every time but probably about 60% of the time with the following output (when it does fails it always fails in the same way):

length mismatch 244544 219195

The result of not reading all the data is that pickle.loads hangs.

I'd really like to use this tool but can't until I can get this fixed.

Python 3 support?

I'm upgrading a Python 2.7 application to Python 3, using https://pyup.io to track dependencies which are not up-to-date or not Python 3 compatible. https://pyup.io tells me that errand-boy doesn't have Python 3 support, presumably because its setup.py doesn't have classifiers like:

         "Programming Language :: Python :: 2",
         "Programming Language :: Python :: 2.7",
         "Programming Language :: Python :: 3",
         "Programming Language :: Python :: 3.5",
         "Programming Language :: Python :: 3.6",
         "Programming Language :: Python :: 3.7",

I understand that https://pyup.io looks at https://pypi.org/pypi/errand-boy/json to check for Python 3 support.

Are there any plans to address this?

Eventlet dependency

We encountered an error that we believe we can fix by removing the eventlet dependency from our system (it interacts poorly with gevent, which we use: gevent/gevent#577), and errand-boy is the only source of that dependency for us.

I was wondering if you would consider removing that dependency from setup.py. I looked over the codebase, and it appears to be used only in errand_boy/transports/mock.py, which made me think it may not be a core dependency. If that's the case, could it be moved to tests_require instead of install_requires?

stdin

Can errand-boy spawn a subprocess that can have data written to it via stdin?Can errand-boy spawn a subprocess that can have data written to it via stdin?

"error: [Errno 2] No such file or directory" when using `run_cmd`

I found the package by searching solutions on subprocess.Popen() out of memory. Basically I have some shell cmds to run in Python scripts, like './XX.exe', rm *.txt, 'mkdir -p XX/XXX'.

Maybe I didn't understand it right, but when I was testing the example, I got the following error:

In [1]: from errand_boy.transports.unixsocket import UNIXSocketTransport

In [2]: errand_boy_transport = UNIXSocketTransport()

In [3]: errand_boy_transport.run_cmd('ls')
---------------------------------------------------------------------------
error                                     Traceback (most recent call last)
<ipython-input-3-c7aa72265fc5> in <module>()
----> 1 errand_boy_transport.run_cmd('ls')

/home/XXXXX/anaconda2/lib/python2.7/site-packages/errand_boy/transports/base.pyc in run_cmd(self, command_string)
    437
    438     def run_cmd(self, command_string):
--> 439         with self.get_session() as session:
    440             subprocess = session.subprocess
    441

/home/XXXXX/anaconda2/lib/python2.7/site-packages/errand_boy/transports/base.pyc in get_session(self)
    434
    435     def get_session(self):
--> 436         return ClientSession(self)
    437
    438     def run_cmd(self, command_string):

/home/XXXXX/anaconda2/lib/python2.7/site-packages/errand_boy/transports/base.pyc in __init__(self, transport)
     29     def __init__(self, transport):
     30         self.transport = transport
---> 31         self.connection = transport.client_get_connection()
     32         self._closed = True
     33

/home/XXXXX/anaconda2/lib/python2.7/site-packages/errand_boy/transports/unixsocket.pyc in client_get_connection(self)
     81         clientsocket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
     82
---> 83         clientsocket.connect(self.socket_path)
     84
     85         return clientsocket

/home/XXXXX/anaconda2/lib/python2.7/socket.pyc in meth(name, self, *args)
    226
    227 def meth(name,self,*args):
--> 228     return getattr(self._sock,name)(*args)
    229
    230 for _m in _socketmethods:

error: [Errno 2] No such file or directory

As you can see I'm in Anaconda Python 2.7, the package was installed by pip

Does anyone knows how to deal with the problem ?

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.