The back story is that the older APIs that Python comes with -- os.popen and os.system -- are deprecated. Programmers are urged to use the "subprocess" module instead. Although this doesn't have the problems of the original functions, it has a rather arcane interface, in particular if you want to read the output (stdout or stderr) of a subprocess.
"envoy" seems to aim at fixing this, by providing sane defaults and being optimized for the common case. However, these defaults have drawbacks of their own.
1. envoy defaults to keeping the process output in memory, as a giant string. This can be a bad choice with regard to memory usage and performance.
2. You can run several processes in a pipe using ("cat foo | grep bla"). But otherwise as far as I can see, run() ignores regular Shell semantics, such as quotes. I imagine this can lead to unexpected results. The amount of data passed from one process to the next is capped at 10 MB -- recipe for bugs that are hard to find.
3. subprocess.call() accepts an array in the style of ["ls", "-l", "/mnt/My SD card"]. This has obvious advantages over having to deal with escaping shell characters. A good API should preserve this advantage over os.system().
4. The defaults cannot be overridden, and no preperations have been made to allow changing them. Of course this can be changed in the future. However, one of the reasons the subprocess.* API is convoluted is that it allows all kinds of flexibility, much of which is needed in many serious programs. It may be difficult to add this flexibility to envoy at a later stage. The point is that a flexible API is hard.
None of this is to discourage this initiative, which seems to me a much-needed improvement over Python's built-in API. Also, with a version number as low as 0.0.2, there is probably little need to worry about API compatibility.
Unless you're running on Windows, in which case IME it will corrupt your carefully constructed parameters in completely inappropriate ways that can be debugged only at the cost of (a) changing the call() to execute a script that dumps the actual parameters supplied verbatim, and (b) at least an hour of your life that you're never getting back.
This "feature" is about one step above MS Word's default autoreplace behaviour in irritation level. What happened to "Explicit is better than implicit" and "Special cases aren't special enough to break the rules"?
oputput = subprocess.check_output(my_command)edit: If anyone wants to see a real-world refactor from HTTPLib/2 to requests, I did so with Pysolr here: https://github.com/mattdeboard/pysolr/commit/db63d8910dec42d...
I had to highlight the text and drag downwards in order to see the content. But it was annoying having to do this for every slide with a lot of content.
Otherwise, these libraries seem really useful. Thanks for this.
Whats so wrong with just sticking a bunch of static slides on a page one after another?
Libraries tend to move more quickly than the language and interpreter/compiler. Tying them together, while convenient, often leads to rot, clunky libraries, slow moving updates, and libraries being build to the interpreter/compiler instead of to the needs of the users.
I would like to see instead a somewhat canonical (widely accepted) list of the highest quality libraries for a given set of needs, with information and pro/con/caveats listed for each, instead of them being included in the mainline trunk.
I really applaud what Kenneth Reitz has been doing lately.
-- a very happy user of the `requests` library
I'd like to see some community effort to build a collection of similar "better than the standard" libs.
Which, at some point, could replace the standard libs. Or be the de facto standard, a pip install call away...
from subprocess import check_output as qx
output = qx(['command', 'arg1'])And those functions are more convenient than the default behavior of backticks, because they handle for you raising an exception if the subprocess fails.
Python:
import subprocess
output = subprocess.check_output('command')
Perl: $output = `command`
die "failed: $output" if $?If you have a look at the older libraries, most of them were written in a procedural style. Not only that, it is very amenable to testing in the REPL.
import smtplib
s=smtplib.SMTP("localhost")
s.sendmail("me@my.org",tolist,msg)
note the absence of doers like "Adapters", "Handler", "Manager", "Factory"If you have a look at the XML library, roughly when "patterns" became popular, this style of thinking infested standard library contributions. It also coincides with a time when camelCased function names crept into the python standard library.
Here's one in xml/dom/pulldom.py:
self.documentFactory = documentFactory
Once you see this, you know you are in for some subclassing. You can no longer REPL your way to figure out how things work, and you now have to consult the manual.Here's more pain from libraries of the same era, some of these I'd argue un-Pythonic:
#xml/sax/xmlreader.py:
def setContentHandler(self, handler):
#wsgiref/simple_server.py:
class ServerHandler(SimpleHandler):
#urllib2.py:
class HTTPDigestAuthHandler(BaseHandler,
AbstractDigestAuthHandler):
The last example is especially jarring. Abstract classes have a place in strongly typed world to declare interfaces, and help with vtable-style dispatch. In Python, where you have duck-typing and monkey patching, a class that virtually "does nothing" on its own stands out like a guy in a tux at a beach party.Even logging is infected by the same over-patterning. logging/__init__.py:
class StreamHandler(Handler)
LoggerAdapter(someLogger, dict(p1=v1, p2="v2"))
"Managers" - what a pain when plain function handles would have done the job. Does this name even tell you what task the class performs? #multiprocessing/managers.py:
class BaseManager(object)
If anyone remembers, Java had to do OO in a big-style with OO everywhere -- there were no alternatives.Initially, buttons had to be subclassed just to handle click events, since functions were not first class objects. Then someone came up with a MouseListener interface, which proved too unwieldy to handle a single click. So the MouseEventAdapters came into being.
Therefore, to handle a click in a "pattern" manner involves
an anonymous class
which subclasses MouseAdapter
which implements MouseListener,
which overrides MouseClick.
Publishing how industry solves this problem of "MouseClick" over and over as a pattern [design pattern is a general reusable solution to a commonly occurring problem within a given context in software design] only gives legitimacy to an approach that has dubious wider applicability.
Heavens help the future developers who are forced to do it because it is now recognized as being industrially "good practice" and codified in a reknowned book.
It isn't!
It was a style that was forced by the constraints of a language.
This is neither pythonic nor necessary:
panel.addMouseListener
(
new MouseAdapter ()
{
public void mouseEntered (MouseEvent e) {
System.out.println (e.toString ());
}
}
);
Embracing "foolish, unschooled" thinking, this would be rendered in Python as: def mouseEntered(event):
print event
panel.mouseEntered = mouseEntered
or for multiple event handlers panel.mouseEntered.append(mouseEntered)
This style of API again allows effective exploration on the REPL. > If anyone remembers, Java had to do OO in a
> big-style with OO everywhere -- there were no
> alternatives.
You can write Java that isn't heavily OO, but you have to implement alternatives to sections of the stdlib that most people assume or take for granted.Related to what you're saying about the GUI, I'd be interested to see a detailed summary of what the Lighthouse people did, and how it was different to Java. I've found that NeXT tradition stuff - despite claims that it's heavily OO - in facat tends to err away from subclassing towards composition. I suspect the Lighthouse interface patterns did too.
I agree with the author's goals of making common tasks easier and more obvious. urllib2 is an easy target, as it was added to the standard library over a decade ago, long before REST was something people talked about. The best tools for packaging, versioning, and testing have always been a bit ambiguous in any language, including Python.
However, the author points out something that has always bothered me about Python: it is way harder to start a subprocess with an external command in Python than almost any other language. This has been true whether using sys or os or even subprocess, which is quite recent.
I always felt that this had something to do with the constant warnings in the documentation about how a pipe between the subprocess and the Python process might fill and cause the subprocess to block. Or how running the program through shell rather than exec or something might cause some sort of security issue. Are these real issues that other languages ignore in the name of user convenience, or has Python just never been able to make the right API (as the author seems to argue)?
There are lots of interesting corner cases, for example how to join stdout and stderr properly without blocking on one stream while the other is overflowing.
On the other hand, almost nobody ever needs this. Ruby's "output = `command`" probably covers 90% of the use cases with the most trivial API imaginable. The hard part obviously is exposing the advanced functionality without compromising on the simplicity.
Almost all programming communities can learn a lot from Ruby's "if it's too hard, you're not cheating enough" approach (dhh quote I believe). Yes, the process could return an exabyte of stdout data, but do you really care? Is that really the problem this API should try to solve, with all special cases? That's not good computer science practice, but surprisingly effective.
There's no fundamental problem that's stopped Python from doing this before. For some reason, all of the ways to spawn a subprocess in Python have tried to map almost directly to the underlying C API... which is pretty awful.
For quick tasks and scripts, I've found subprocess.check_call, and subprocess.check_output with shell=True are great tools for spawning subprocesses and quickly grabbing output. They're pretty straightforward to use.
After that use virtualenv with virtualenvwrapper.
Esp. the "installing python" one. Just use your package manager to install all the versions you need.
And for "Packaging and Dependencies", just use pip.
And installing packages into the system python (if that's what you're suggesting) is the path to madness. It's much better to use virtualenvs you can throw away at will. All in all, it's usually best just to leave the system Python alone to avoid causing problems with any other packages that may depend on it being in a consistent state.
The standard library needs a reboot. Why not do it in Python 3? Nobody's using it yet anyway ;-)