setup.py

Summary:
Create a simple setup.py file from the instructions at
https://packaging.python.org/en/latest/distributing.html. The
`package_data` field doesn't seem to like directories, so we need to
walk the paths under `./peru/resources` to include everything.

Right now we're not including anything under third_party. Instead we
pull those in as dependencies from PyPI. Not sure if this is what we'll
do in the long term, but it's nice that it works.

Test Plan:
Ran `python3 setup.py install --user` and confirmed that peru works.
Then `cd tests; python3 -m unittest` to confirm that tests pass against
the installed version.

Reviewers: sean

Reviewed By: sean

Differential Revision: https://phabricator.buildinspace.com/D125
create an Arch PKGBUILD

Summary:
Also including a little script that makes it easier to package your
local copy of the sources (instead of the default, cloning a new repo to
package).

Reviewers: sean

Reviewed By: sean

Differential Revision: https://phabricator.buildinspace.com/D126
use ProactorEventLoop on Windows

Summary:
The default event loop on Windows doesn't support subprocesses, so we
need to set this. Unfortunately the event loop is a global, and there's
a bit of a race condition to set it before anything tries to read it.
(In particular, instantiating an asyncio Lock or Semaphore grabs a
reference to the global loop.) We set it here at import time, which
works fine for now, but it makes me worry that some future code
(probably in tests) isn't going to import our `async` module, and will
run into bugs as a result. I'd love to have a better place to put this.

Reviewers: sean

Reviewed By: sean

Differential Revision: https://phabricator.buildinspace.com/D109
execute plugin processes as shell commands

Summary:
Windows doesn't have anything like shebang lines. Instead, the shell
figures out what interpreter to use based on a file's extension. That
means plain (safe) exec doesn't work for Python scripts on Windows.

Unfortunately, if you do something unwise like setting the default
program for .py files to Notepad++, peru is going to start opening your
editor instead of running plugins. I definitely did that and had to
debug it, and it sucks. But I don't know another way to make Python
scripts work without actually hardcoding the .py extension ourselves,
which doesn't seem right. (Should we do the same for Ruby and Perl
and...) Comments appreciated.

Reviewers: sean

Differential Revision: https://phabricator.buildinspace.com/D110
don't assume decoders have the buffer attribute

Summary:
The default encoding for cmd.exe is "cp437", the original DOS codepage,
and the decoder for that is one of the ones that doesn't have an
accessible buffer attribute.

Reviewers: sean

Differential Revision: https://phabricator.buildinspace.com/D111
add --catch to the unit tests

Summary:
This lets us hit Ctrl-C and still get results from tests that have run
so far.

Reviewers: sean

Differential Revision: https://phabricator.buildinspace.com/D112
peru.bat and test.bat

Summary:
Launcher scripts for Windows. These probably shouldn't live in the top
level, but I haven't done the work to make them run from other
directories yet. I should do that before this lands.

Reviewers: sean

Differential Revision: https://phabricator.buildinspace.com/D113
unify newlines in subprocess output

Summary:
Because all asyncio subprocess output is read in binary mode, we don't
get universal newlines for free. But it's the right thing to do, because
we do all our printing with strings in text mode, which translates "\n"
back into the platform-appropriate line separator. So for example,
"\r\n" in a string on Windows will become "\r\r\n" when it gets printed.
This function ensures that all newlines are represented as "\n"
internally, which solves that problem and also helps our tests work on
Windows. Right now we only handle Windows, but we can expand this if
there's ever another newline style we have to support.

Reviewers: sean

Differential Revision: https://phabricator.buildinspace.com/D114
use pathlib when dealing with paths in tests, and add unit tests

Summary:
Before this change, tons of our tests failed on Windows because they
hardcoded paths of the form "a/b/c". Then `os.walk` spat out "a\b\c" and
everything would die. Pathlib solves this nicely.

I was tempted to make the whole shared test library (including e.g.
`create_dir`) return nothing but pathlib paths. Unfortunately, that
required callers to re-stringify paths before passing them to things
like subprocess `cwd` and `env` parameters. Getting rid of
`os.path.join` was nice, but it was more trouble than it was worth.

Reviewers: sean

Differential Revision: https://phabricator.buildinspace.com/D115
stop hardcoding the temp root

Summary: The hardcoded value wasn't appropriate for Windows.

Reviewers: sean

Differential Revision: https://phabricator.buildinspace.com/D116
stop using shell commands in tests

Summary:
Lots of our test code dealing with repos was running commands as shell
strings for convenience. Even though we stuck to simple commands, we
still ran into issues with single-quoted parameters not working on
Windows. Skipping the shell entirely and using plain old exec solves
all these problems.

Reviewers: sean

Differential Revision: https://phabricator.buildinspace.com/D117
use pathlib for building file:// URIs

Summary:
The rules for building file URIs on Windows are a little more
complicated than just sticking `file://` in front of a path. Luckily
pathlib has a method that does everything right.

Reviewers: sean

Differential Revision: https://phabricator.buildinspace.com/D118
add __str__ for PrintableError

Summary: This makes it easier to read plugin errors that happen during tests.

Reviewers: sean

Differential Revision: https://phabricator.buildinspace.com/D119
stop using TemporaryNamedFile

Summary:
It looks like Windows takes an exclusive write lock on
`TemporaryNamedFile`s when they're created. We want to be able to open
temp files in a context manager than cleans them up automatically, which
is causing errors when the file is opened again for actual writing. It's
simpler to just use the temporary directory context manager and create
an unmanaged file in it.

Reviewers: sean

Differential Revision: https://phabricator.buildinspace.com/D120
skip tests with build commands on Windows

Summary:
Eventually we might want some mechanism to allow multiple versions of
build commands, so that a rule can work on both Windows and POSIX. For
now, we'll just skip the tests that rely on build commands when we're
running on Windows.

https://github.com/buildinspace/peru/issues/17

Reviewers: sean

Differential Revision: https://phabricator.buildinspace.com/D121
skip testing the rsync plugin on Windows

Summary:
The rsync plugin is written in bash, so it won't work properly on Windows.
A couple field validation tests also depended on rsync, but those can use
the git plugin instead.

Reviewers: sean

Differential Revision: https://phabricator.buildinspace.com/D122
update asyncio to the Python 3.4.2 version

Summary:
This version does a better job of detecting unsafe operations across
loops/threads in `PYTHONASYNCIODEBUG` mode, which is nice.

Reviewers: sean

Differential Revision: https://phabricator.buildinspace.com/D123
silence an autocrlf warning on Windows in the git submodules test

Reviewers: sean

Differential Revision: https://phabricator.buildinspace.com/D124