Commit 23667d28 by Sébastien Eustace Committed by GitHub

New installation method (#378)

* Implement the new installer (UNIX)

* Add a -y/—yes option to the installer

* Update Travis config

* Add Windows support to the custom installer

* Build releases automatically and use them to install

* Do not build automatically for now

* Use docker to build releases for linux

* Update documentation

* Use Github to download releases
parent 004514d7
...@@ -7,6 +7,8 @@ environment: ...@@ -7,6 +7,8 @@ environment:
- PYTHON: "C:/Python27-x64" - PYTHON: "C:/Python27-x64"
- PYTHON: "C:/Python35-x64" - PYTHON: "C:/Python35-x64"
- PYTHON: "C:/Python36-x64" - PYTHON: "C:/Python36-x64"
- PYTHON: "C:/Python37-x64"
install: install:
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
...@@ -15,8 +17,8 @@ install: ...@@ -15,8 +17,8 @@ install:
# about it being out of date. # about it being out of date.
- "python -m pip install --disable-pip-version-check --user --upgrade pip" - "python -m pip install --disable-pip-version-check --user --upgrade pip"
# Create poetry virtualenv # Installing Poetry
- "python -m pip install poetry --pre" - "python -m pip install poetry --pre -U"
# Install dependencies # Install dependencies
- "poetry install -v" - "poetry install -v"
......
...@@ -29,9 +29,9 @@ setup.cfg ...@@ -29,9 +29,9 @@ setup.cfg
MANIFEST.in MANIFEST.in
/setup.py /setup.py
/docs/site/* /docs/site/*
pyproject.lock
/tests/fixtures/simple_project/setup.py /tests/fixtures/simple_project/setup.py
/tests/fixtures/project_with_extras/setup.py /tests/fixtures/project_with_extras/setup.py
.mypy_cache .mypy_cache
.venv .venv
/releases/*
...@@ -7,26 +7,25 @@ stages: ...@@ -7,26 +7,25 @@ stages:
cache: cache:
pip: true pip: true
directories: directories:
- $HOME/.cache/pypoetry - "$HOME/.cache/pypoetry"
- $HOME/.cache/pre-commit - "$HOME/.cache/pre-commit"
install: install:
- pip install poetry --pre - pip install pip -U
- pip install poetry --pre -U
- poetry install -v - poetry install -v
script: pytest -q tests/ script: pytest -q tests/
jobs: jobs:
include: include:
- python: "2.7" - python: '2.7'
- python: "3.4" - python: '3.5'
- python: "3.5" - python: '3.6'
- python: "3.6" - stage: linting
python: '3.6'
- stage: linting install:
python: "3.6" - pip install pre-commit
install: - pre-commit install-hooks
- pip install pre-commit script:
- pre-commit install-hooks - pre-commit run --all-files
script:
- pre-commit run --all-files
# This file is part of Poetry
# https://github.com/sdispater/poetry
# Licensed under the MIT license:
# http://www.opensource.org/licenses/MIT-license
# Copyright (c) 2018 Sébastien Eustace
POETRY_RELEASE := $$(sed -n -E "s/__version__ = '(.+)'/\1/p" poetry/__version__.py)
# lists all available targets
list:
@sh -c "$(MAKE) -p no_targets__ | \
awk -F':' '/^[a-zA-Z0-9][^\$$#\/\\t=]*:([^=]|$$)/ {\
split(\$$1,A,/ /);for(i in A)print A[i]\
}' | grep -v '__\$$' | grep -v 'make\[1\]' | grep -v 'Makefile' | sort"
# required for list
no_targets__:
# install all dependencies
setup: setup-python
# test your application (tests in the tests/ directory)
test:
@py.test --cov=poetry --cov-config .coveragerc tests/ -sq
release: build linux_release osx_release
build:
@poetry build
@python sonnet make:release
publish:
@poetry publish
wheel:
@poetry build -v
linux_release:
docker pull quay.io/pypa/manylinux1_x86_64
docker run --rm -v `pwd`:/io quay.io/pypa/manylinux1_x86_64 /io/make-linux-release.sh
# run tests against all supported python versions
tox:
@tox
...@@ -20,30 +20,70 @@ recommended way of installing `poetry`. ...@@ -20,30 +20,70 @@ recommended way of installing `poetry`.
curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python
``` ```
Alternatively, you can download the `get-poetry.py` file and execute it separately. !!! note
If you want to install prerelease versions, you can do so by passing `--preview` to `get-poetry.py`: You only need to install Poetry once. It will automatically pick up the current
Python version and use it to [create virtualenvs](/docs/basic-usage/#poetry-and-virtualenvs) accordingly.
```bash The installer installs the `poetry` tool to Poetry's `bin` directory.
python get-poetry.py --preview On Unix it is located at `$HOME/.poetry/bin` and on Windows at `%USERPROFILE%\.poetry\bin`.
```
Similarly, if you want to install a specific version, you can use `--version`: This directory will be in your `$PATH` environment variable,
which means you can run them from the shell without further configuration.
Open a new shell and type the following:
```bash ```bash
python get-poetry.py --version 0.7.0 poetry --version
``` ```
If you see something like `Poetry 0.11.4` then you are ready to use Poetry.
If you decide Poetry isn't your thing, you can completely remove it from your system
by running the installer again with the `--uninstall` option.
!!!note !!!note
Using `pip` to install `poetry` is also possible. Alternatively, you can download the `get-poetry.py` file and execute it separately.
If you want to install prerelease versions, you can do so by passing `--preview` to `get-poetry.py`:
```bash
python get-poetry.py --preview
```
Similarly, if you want to install a specific version, you can use `--version`:
```bash ```bash
pip install --user poetry python get-poetry.py --version 0.7.0
``` ```
Be aware, however, that it will also install poetry's dependencies Note that the installer does not support Poetry releases < 12.0.
which might cause conflicts.
### Alternative installation methods (not recommended)
#### Installing with `pip`
Using `pip` to install Poetry is possible.
```bash
pip install --user poetry
```
!!!warning
Be aware that it will also install Poetry's dependencies
which might cause conflicts with other packages.
#### Installing with `pipsi`
Using [`pipsi`](https://github.com/mitsuhiko/pipsi) to install Poetry is also possible.
```bash
pipsi install poetry
```
Make sure your installed version of `pipsi` is at least version `0.10`,
otherwise Poetry will not function properly. You can get it from its
[Github repository](https://github.com/mitsuhiko/pipsi).
## Updating `poetry` ## Updating `poetry`
...@@ -67,6 +107,11 @@ to `self:update`. ...@@ -67,6 +107,11 @@ to `self:update`.
poetry self:update 0.8.0 poetry self:update 0.8.0
``` ```
!!!note
The `self:update` command will only work if you used the recommended
installer to install Poetry.
## Enable tab completion for Bash, Fish, or Zsh ## Enable tab completion for Bash, Fish, or Zsh
...@@ -91,7 +136,7 @@ poetry completions zsh > ~/.zfunc/_poetry ...@@ -91,7 +136,7 @@ poetry completions zsh > ~/.zfunc/_poetry
!!! note !!! note
You may need to restart your shell in order for the changes to take effect. You may need to restart your shell in order for the changes to take effect.
For `zsh`, you must then add the following line in your `~/.zshrc` before `compinit`: For `zsh`, you must then add the following line in your `~/.zshrc` before `compinit`:
```bash ```bash
......
...@@ -4,38 +4,60 @@ in isolation from the rest of the system. ...@@ -4,38 +4,60 @@ in isolation from the rest of the system.
It does, in order: It does, in order:
- Downloads the latest stable version of poetry. - Downloads the latest stable (or pre-release) version of poetry.
- Checks if the _vendor directory is empty. - Downloads all its dependencies in the poetry/_vendor directory.
- If the _vendor directory is not empty, empties it. - Copies it and all extra files in $POETRY_HOME.
- Installs all dependencies in the _vendor directory - Updates the PATH in a system-specific way.
This ensure that poetry will look for its dependencies inside There will be a `poetry` script that will be installed in $POETRY_HOME/bin
the _vendor directory without tampering with the base system. which will act as the poetry command but is slightly different in the sense
that it will use the current Python installation.
Note, however, that installing poetry via pip will still work, What this means is that one Poetry installation can serve for multiple
since if poetry does not find the dependencies in the _vendor Python versions.
directory, it will look for them in the base system.
""" """
import argparse import argparse
import hashlib
import json import json
import os import os
import platform import platform
import re import re
import shutil import shutil
import stat
import subprocess import subprocess
import sys import sys
import tarfile
import tempfile import tempfile
from contextlib import contextmanager from contextlib import contextmanager
from email.parser import Parser
from functools import cmp_to_key from functools import cmp_to_key
from glob import glob from gzip import GzipFile
from io import UnsupportedOperation
try: try:
from urllib.error import HTTPError
from urllib.request import urlopen from urllib.request import urlopen
except ImportError: except ImportError:
from urllib2 import HTTPError
from urllib2 import urlopen from urllib2 import urlopen
try:
input = raw_input
except NameError:
pass
try:
try:
import winreg
except ImportError:
import _winreg as winreg
except ImportError:
winreg = None
WINDOWS = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt")
FOREGROUND_COLORS = { FOREGROUND_COLORS = {
"black": 30, "black": 30,
...@@ -85,6 +107,7 @@ STYLES = { ...@@ -85,6 +107,7 @@ STYLES = {
"info": style("green", None, None), "info": style("green", None, None),
"comment": style("yellow", None, None), "comment": style("yellow", None, None),
"error": style("red", None, None), "error": style("red", None, None),
"warning": style("yellow", None, None),
} }
...@@ -127,9 +150,120 @@ def temporary_directory(*args, **kwargs): ...@@ -127,9 +150,120 @@ def temporary_directory(*args, **kwargs):
shutil.rmtree(name) shutil.rmtree(name)
def expanduser(path):
"""
Expand ~ and ~user constructions.
Includes a workaround for http://bugs.python.org/issue14768
"""
expanded = os.path.expanduser(path)
if path.startswith("~/") and expanded.startswith("//"):
expanded = expanded[1:]
return expanded
HOME = expanduser("~")
POETRY_HOME = os.path.join(HOME, ".poetry")
POETRY_BIN = os.path.join(POETRY_HOME, "bin")
POETRY_ENV = os.path.join(POETRY_HOME, "env")
POETRY_LIB = os.path.join(POETRY_HOME, "lib")
POETRY_LIB_BACKUP = os.path.join(POETRY_HOME, "lib-backup")
BIN = """#!/usr/bin/env python
import glob
import sys
import os
lib = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "lib"))
sys.path.insert(0, lib)
if __name__ == "__main__":
from poetry.console import main
main()
"""
BAT = "@echo off\r\npython %USERPROFILE%/.poetry/bin/poetry %*\r\n"
PRE_MESSAGE = """# Welcome to {poetry}!
This will download and install the latest version of {poetry},
a dependency and package manager for Python.
It will add the `poetry` command to {poetry}'s bin directory, located at:
{poetry_home_bin}
{platform_msg}
You can uninstall at any time with `poetry self:uninstall`,
or by executing this script with the --uninstall option,
and these changes will be reverted.
"""
PRE_UNINSTALL_MESSAGE = """# We are sorry to see you go!
This will uninstall {poetry}.
It will remove the `poetry` command from {poetry}'s bin directory, located at:
{poetry_home_bin}
This will also remove {poetry} from your system's PATH.
"""
PRE_MESSAGE_UNIX = """This path will then be added to your `PATH` environment variable by
modifying the profile file{plural} located at:
{rcfiles}"""
PRE_MESSAGE_WINDOWS = """This path will then be added to your `PATH` environment variable by
modifying the `HKEY_CURRENT_USER/Environment/PATH` registry key."""
PRE_MESSAGE_NO_MODIFY_PATH = """This path needs to be in your `PATH` environment variable,
but will not be added automatically."""
POST_MESSAGE_UNIX = """{poetry} ({version}) is installed now. Great!
To get started you need {poetry}'s bin directory ({poetry_home_bin}) in your `PATH`
environment variable. Next time you log in this will be done
automatically.
To configure your current shell run `source {poetry_home_env}`
"""
POST_MESSAGE_WINDOWS = """{poetry} ({version}) is installed now. Great!
To get started you need Poetry's bin directory ({poetry_home_bin}) in your `PATH`
environment variable. Future applications will automatically have the
correct environment, but you may need to restart your current shell.
"""
POST_MESSAGE_UNIX_NO_MODIFY_PATH = """{poetry} ({version}) is installed now. Great!
To get started you need {poetry}'s bin directory ({poetry_home_bin}) in your `PATH`
environment variable.
To configure your current shell run `source {poetry_home_env}`
"""
POST_MESSAGE_WINDOWS_NO_MODIFY_PATH = """{poetry} ({version}) is installed now. Great!
To get started you need Poetry's bin directory ({poetry_home_bin}) in your `PATH`
environment variable. This has not been done automatically.
"""
class Installer: class Installer:
CURRENT_PYTHON = sys.executable CURRENT_PYTHON = sys.executable
CURRENT_PYTHON_VERSION = sys.version_info[:2]
METADATA_URL = "https://pypi.org/pypi/poetry/json" METADATA_URL = "https://pypi.org/pypi/poetry/json"
VERSION_REGEX = re.compile( VERSION_REGEX = re.compile(
"v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?" "v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?"
...@@ -141,15 +275,59 @@ class Installer: ...@@ -141,15 +275,59 @@ class Installer:
"(?:\+[^\s]+)?" "(?:\+[^\s]+)?"
) )
def __init__(self, version=None, preview=False): BASE_URL = "https://github.com/sdispater/poetry/releases/download/"
def __init__(
self,
version=None,
preview=False,
force=False,
accept_all=False,
base_url=BASE_URL,
):
self._version = version self._version = version
self._preview = preview self._preview = preview
self._force = force
self._modify_path = True
self._accept_all = accept_all
self._base_url = base_url
def allows_prereleases(self): def allows_prereleases(self):
return self._preview return self._preview
def run(self): def run(self):
print(colorize("info", "Retrieving metadata")) version, current_version = self.get_version()
if version is None:
return 0
self.customize_install()
self.display_pre_message()
self.ensure_home()
try:
self.install(version, upgrade=current_version is not None)
except subprocess.CalledProcessError as e:
print(colorize("error", "An error has occured: {}".format(str(e))))
print(e.output.decode())
return e.returncode
self.display_post_message(version)
return 0
def uninstall(self):
self.display_pre_uninstall_message()
if not self.customize_uninstall():
return
self.remove_home()
self.remove_from_path()
def get_version(self):
print(colorize("info", "Retrieving Poetry metadata"))
r = urlopen(self.METADATA_URL) r = urlopen(self.METADATA_URL)
metadata = json.loads(r.read().decode()) metadata = json.loads(r.read().decode())
...@@ -175,9 +353,9 @@ class Installer: ...@@ -175,9 +353,9 @@ class Installer:
) )
if self._version and self._version not in releases: if self._version and self._version not in releases:
print(colorize("error", "Version {} does not exist".format(self._version))) print(colorize("error", "Version {} does not exist.".format(self._version)))
return 1 return None, None
version = self._version version = self._version
if not version: if not version:
...@@ -190,132 +368,409 @@ class Installer: ...@@ -190,132 +368,409 @@ class Installer:
break break
try: current_version = None
import poetry if os.path.exists(POETRY_LIB):
with open(os.path.join(POETRY_LIB, "poetry", "__version__.py")) as f:
version_content = f.read()
poetry_version = poetry.__version__ current_version_re = re.match(
except ImportError: '(?ms).*__version__ = "(.+)".*', version_content
poetry_version = None )
if not current_version_re:
print(
colorize(
"warning",
"Unable to get the current Poetry version. Assuming None",
)
)
else:
current_version = current_version_re.group(1)
if poetry_version == version: if current_version == version and not self._force:
print("Latest version already installed.") print("Latest version already installed.")
return 0 return None, current_version
return version, current_version
def customize_install(self):
if not self._accept_all:
print("Before we start, please answer the following questions.")
print("You may simple press the Enter key to keave unchanged.")
modify_path = input("Modify PATH variable? ([y]/n) ") or "y"
if modify_path.lower() in {"n", "no"}:
self._modify_path = False
print("")
def customize_uninstall(self):
if not self._accept_all:
print()
uninstall = (
input("Are you sure you want to uninstall Poetry? (y/[n]) ") or "n"
)
if uninstall.lower() not in {"y", "yes"}:
return False
print("")
return True
def customize_install(self):
if not self._accept_all:
print("Before we start, please answer the following questions.")
print("You may simple press the Enter key to keave unchanged.")
modify_path = input("Modify PATH variable? ([y]/n) ") or "y"
if modify_path.lower() in {"n", "no"}:
self._modify_path = False
print("")
def ensure_home(self):
"""
Ensures that $POETRY_HOME exists or create it.
"""
if not os.path.exists(POETRY_HOME):
os.mkdir(POETRY_HOME, 0o755)
def remove_home(self):
"""
Removes $POETRY_HOME.
"""
if not os.path.exists(POETRY_HOME):
return
shutil.rmtree(POETRY_HOME)
def install(self, version, upgrade=False):
"""
Installs Poetry in $POETRY_HOME.
"""
print("Installing version: " + colorize("info", version)) print("Installing version: " + colorize("info", version))
self.make_lib(version)
self.make_bin()
self.make_env()
self.update_path()
return 0
def make_lib(self, version):
"""
Packs everything into a single lib/ directory.
"""
if os.path.exists(POETRY_LIB_BACKUP):
shutil.rmtree(POETRY_LIB_BACKUP)
# Backup the current installation
if os.path.exists(POETRY_LIB):
shutil.copytree(POETRY_LIB, POETRY_LIB_BACKUP)
shutil.rmtree(POETRY_LIB)
try: try:
return self.install(version) self._make_lib(version)
except subprocess.CalledProcessError as e: except Exception:
print(colorize("error", "An error has occured: {}".format(str(e)))) if not os.path.exists(POETRY_LIB_BACKUP):
print(e.output.decode()) raise
return e.returncode shutil.copytree(POETRY_LIB_BACKUP, POETRY_LIB)
shutil.rmtree(POETRY_LIB_BACKUP)
def install(self, version): raise
# Most of the work will be delegated to pip finally:
with temporary_directory(prefix="poetry-installer-") as dir: if os.path.exists(POETRY_LIB_BACKUP):
dist = os.path.join(dir, "dist") shutil.rmtree(POETRY_LIB_BACKUP)
print(" - Getting dependencies")
try:
self.call(
self.CURRENT_PYTHON,
"-m",
"pip",
"install",
"poetry=={}".format(version),
"--target",
dist,
)
except subprocess.CalledProcessError as e:
if "must supply either home or prefix/exec-prefix" in e.output.decode():
# Homebrew Python and possible other installations
# We workaround this issue by temporarily changing
# the --user directory
original_user = os.getenv("PYTHONUSERBASE")
os.environ["PYTHONUSERBASE"] = dir
self.call(
self.CURRENT_PYTHON,
"-m",
"pip",
"install",
"poetry=={}".format(version),
"--user",
"--ignore-installed",
)
if original_user is not None: def _make_lib(self, version):
os.environ["PYTHONUSERBASE"] = original_user # We get the payload from the remote host
else: url = self._base_url + "{}/".format(version)
del os.environ["PYTHONUSERBASE"] name = "poetry-{}-{}.tar.gz".format(version, sys.platform)
checksum = "poetry-{}-{}.sha256sum".format(version, sys.platform)
# Finding site-package directory
lib = os.path.join(dir, "lib")
lib_python = list(glob(os.path.join(lib, "python*")))[0]
site_packages = os.path.join(lib_python, "site-packages")
shutil.copytree(site_packages, dist)
else:
raise
print(" - Vendorizing dependencies")
poetry_dir = os.path.join(dist, "poetry")
vendor_dir = os.path.join(poetry_dir, "_vendor")
# Everything, except poetry itself, should
# be put in the _vendor directory
for file in glob(os.path.join(dist, "*")):
if (
os.path.basename(file).startswith("poetry")
or os.path.basename(file) == "__pycache__"
):
continue
dest = os.path.join(vendor_dir, os.path.basename(file)) try:
if os.path.isdir(file): r = urlopen(url + "{}".format(checksum))
shutil.copytree(file, dest) except HTTPError as e:
shutil.rmtree(file) if e.code == 404:
else: raise RuntimeError("Could not find {} file".format(checksum))
shutil.copy(file, dest)
os.unlink(file)
wheel_data = os.path.join( raise
dist, "poetry-{}.dist-info".format(version), "WHEEL"
)
with open(wheel_data) as f:
wheel_data = Parser().parsestr(f.read())
tag = wheel_data["Tag"] checksum = r.read().decode()
# Repack everything and install try:
print(" - Installing {}".format(colorize("info", "poetry"))) r = urlopen(_url + "{}".format(name))
except HTTPError as e:
if e.code == 404:
raise RuntimeError("Could not find {} file".format(name))
shutil.make_archive( raise
os.path.join(dir, "poetry-{}-{}".format(version, tag)),
format="zip", meta = r.info()
root_dir=str(dist), size = int(meta["Content-Length"])
) current = 0
block_size = 8192
os.rename( print(
os.path.join(dir, "poetry-{}-{}.zip".format(version, tag)), " - Downloading {} ({:.2f}MB)".format(
os.path.join(dir, "poetry-{}-{}.whl".format(version, tag)), colorize("comment", name), size / 1024 / 1024
) )
)
sha = hashlib.sha256()
with temporary_directory(prefix="poetry-installer-") as dir_:
tar = os.path.join(dir_, name)
with open(tar, "wb") as f:
while True:
buffer = r.read(block_size)
if not buffer:
break
current += len(buffer)
f.write(buffer)
sha.update(buffer)
# Checking hashes
if checksum != sha.hexdigest():
raise RuntimeError(
"Hashes for {} do not match: {} != {}".format(
name, checksum, sha.hexdigest()
)
)
gz = GzipFile(tar, mode="rb")
try:
with tarfile.TarFile(tar, fileobj=gz, format=tarfile.PAX_FORMAT) as f:
f.extractall(POETRY_LIB)
finally:
gz.close()
def make_bin(self):
if not os.path.exists(POETRY_BIN):
os.mkdir(POETRY_BIN, 0o755)
ext = ""
if WINDOWS:
with open(os.path.join(POETRY_BIN, "poetry.bat"), "w") as f:
f.write(BAT)
with open(os.path.join(POETRY_BIN, "poetry"), "w") as f:
f.write(BIN)
if not WINDOWS:
# Making the file executable
st = os.stat(os.path.join(POETRY_BIN, "poetry"))
os.chmod(os.path.join(POETRY_BIN, "poetry"), st.st_mode | stat.S_IEXEC)
def make_env(self):
if WINDOWS:
return
with open(os.path.join(POETRY_HOME, "env"), "w") as f:
f.write(self.get_export_string())
def update_path(self):
"""
Tries to update the $PATH automatically.
"""
if WINDOWS:
return self.add_to_windows_path()
# Updating any profile we can on UNIX systems
export_string = self.get_export_string()
self.call( addition = "\n{}".format(export_string)
self.CURRENT_PYTHON,
"-m", updated = []
"pip", profiles = self.get_unix_profiles()
"install", for profile in profiles:
"--upgrade", if not os.path.exists(profile):
"--no-deps", continue
os.path.join(dir, "poetry-{}-{}.whl".format(version, tag)),
with open(profile, "r") as f:
content = f.read()
if addition not in content:
with open(profile, "a") as f:
f.write(addition)
updated.append(os.path.relpath(profile, HOME))
def add_to_windows_path(self):
try:
old_path = self.get_windows_path_var()
except WindowsError:
old_path = None
if old_path is None:
print(
colorize(
"warning",
"Unable to get the PATH value. It will not be updated automatically",
)
) )
self._modify_path = False
return
new_path = POETRY_BIN
if POETRY_BIN in old_path:
old_path = old_path.replace(POETRY_BIN + ";", "")
if old_path:
new_path += ";"
new_path += old_path
self.set_windows_path_var(new_path)
def get_windows_path_var(self):
with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as root:
with winreg.OpenKey(root, "Environment", 0, winreg.KEY_ALL_ACCESS) as key:
path, _ = winreg.QueryValueEx(key, "PATH")
return path
def set_windows_path_var(self, value):
import ctypes
from ctypes.wintypes import HWND
from ctypes.wintypes import LPARAM
from ctypes.wintypes import LPVOID
from ctypes.wintypes import UINT
from ctypes.wintypes import WPARAM
with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as root:
with winreg.OpenKey(root, "Environment", 0, winreg.KEY_ALL_ACCESS) as key:
winreg.SetValueEx(key, "PATH", 0, winreg.REG_EXPAND_SZ, value)
# Tell other processes to update their environment
HWND_BROADCAST = 0xFFFF
WM_SETTINGCHANGE = 0x1A
SMTO_ABORTIFHUNG = 0x0002
result = ctypes.c_long()
SendMessageTimeoutW = ctypes.windll.user32.SendMessageTimeoutW
SendMessageTimeoutW(
HWND_BROADCAST,
WM_SETTINGCHANGE,
0,
u"Environment",
SMTO_ABORTIFHUNG,
5000,
ctypes.byref(result),
)
def remove_from_path(self):
if WINDOWS:
return self.remove_from_windows_path()
return self.remove_from_unix_path()
def remove_from_windows_path(self):
path = self.get_windows_path_var()
poetry_path = POETRY_BIN
if poetry_path in path:
path = path.replace(POETRY_BIN + ";", "")
if poetry_path in path:
path = path.replace(POETRY_BIN, "")
self.set_windows_path_var(path)
def remove_from_unix_path(self):
pass
def get_export_string(self):
path = POETRY_BIN.replace(os.getenv("HOME", ""), "$HOME")
export_string = 'export PATH="{}:$PATH"'.format(path)
return export_string
def get_unix_profiles(self):
profiles = [os.path.join(HOME, ".profile")]
shell = os.getenv("SHELL")
if "zsh" in shell:
zdotdir = os.getenv("ZDOTDIR", HOME)
profiles.append(os.path.join(zdotdir, ".zprofile"))
bash_profile = os.path.join(HOME, ".bash_profile")
if os.path.exists(bash_profile):
profiles.append(bash_profile)
return profiles
def display_pre_message(self):
if WINDOWS:
home = POETRY_BIN.replace(os.getenv("USERPROFILE", ""), "%USERPROFILE%")
else:
home = POETRY_BIN.replace(os.getenv("HOME", ""), "$HOME")
kwargs = {
"poetry": colorize("info", "Poetry"),
"poetry_home_bin": colorize("comment", home),
}
if not self._modify_path:
kwargs["platform_msg"] = PRE_MESSAGE_NO_MODIFY_PATH
else:
if WINDOWS:
kwargs["platform_msg"] = PRE_MESSAGE_WINDOWS
else:
profiles = [
colorize("comment", p.replace(os.getenv("HOME", ""), "$HOME"))
for p in self.get_unix_profiles()
]
kwargs["platform_msg"] = PRE_MESSAGE_UNIX.format(
rcfiles="\n".join(profiles), plural="s" if len(profiles) > 1 else ""
)
print(PRE_MESSAGE.format(**kwargs))
def display_pre_uninstall_message(self):
kwargs = {
"poetry": colorize("info", "Poetry"),
"poetry_home_bin": colorize(
"comment",
POETRY_BIN.replace(os.getenv("HOME", ""), "$HOME").replace(
os.getenv("USERPROFILE", ""), "%USERPROFILE%"
),
),
}
print(PRE_UNINSTALL_MESSAGE.format(**kwargs))
def display_post_message(self, version):
print("") print("")
print(
"{} ({}) successfully installed!".format( kwargs = {
colorize("info", "poetry"), colorize("comment", version) "poetry": colorize("info", "Poetry"),
"version": colorize("comment", version),
}
if WINDOWS:
message = POST_MESSAGE_WINDOWS
if not self._modify_path:
message = POST_MESSAGE_WINDOWS_NO_MODIFY_PATH
poetry_home_bin = POETRY_BIN.replace(
os.getenv("USERPROFILE", ""), "%USERPROFILE%"
) )
) else:
message = POST_MESSAGE_UNIX
if not self._modify_path:
message = POST_MESSAGE_UNIX_NO_MODIFY_PATH
poetry_home_bin = POETRY_BIN.replace(os.getenv("HOME", ""), "$HOME")
kwargs["poetry_home_env"] = colorize(
"comment", POETRY_ENV.replace(os.getenv("HOME", ""), "$HOME")
)
kwargs["poetry_home_bin"] = colorize("comment", poetry_home_bin)
print(message.format(**kwargs))
def call(self, *args): def call(self, *args):
return subprocess.check_output(args, stderr=subprocess.STDOUT) return subprocess.check_output(args, stderr=subprocess.STDOUT)
...@@ -329,14 +784,28 @@ def main(): ...@@ -329,14 +784,28 @@ def main():
"-p", "--preview", dest="preview", action="store_true", default=False "-p", "--preview", dest="preview", action="store_true", default=False
) )
parser.add_argument("--version", dest="version") parser.add_argument("--version", dest="version")
parser.add_argument(
"-f", "--force", dest="force", action="store_true", default=False
)
parser.add_argument(
"-y", "--yes", dest="accept_all", action="store_true", default=False
)
parser.add_argument(
"--uninstall", dest="uninstall", action="store_true", default=False
)
args = parser.parse_args() args = parser.parse_args()
installer = Installer( installer = Installer(
version=args.version or os.getenv("POETRY_VERSION"), version=args.version or os.getenv("POETRY_VERSION"),
preview=args.preview or os.getenv("POETRY_PREVIEW"), preview=args.preview or os.getenv("POETRY_PREVIEW"),
force=args.force,
accept_all=args.accept_all,
) )
if args.uninstall:
return installer.uninstall()
return installer.run() return installer.run()
......
#!/bin/bash
PYTHON_VERSIONS="cp27-cp27m cp34-cp34m cp35-cp35m cp36-cp36m cp37-cp37m"
cd /io
/opt/python/cp37-cp37m/bin/pip install poetry --pre -U
/opt/python/cp37-cp37m/bin/poetry config settings.virtualenvs.create false
/opt/python/cp37-cp37m/bin/poetry install --no-dev
/opt/python/cp37-cp37m/bin/python sonnet make:release \
-P "2.7:/opt/python/cp27-cp27m/bin/python" \
-P "3.4:/opt/python/cp34-cp34m/bin/python" \
-P "3.5:/opt/python/cp35-cp35m/bin/python" \
-P "3.6:/opt/python/cp36-cp36m/bin/python" \
-P "3.7:/opt/python/cp37-cp37m/bin/python"
cd -
...@@ -3,8 +3,11 @@ import sys ...@@ -3,8 +3,11 @@ import sys
_ROOT = os.path.dirname(os.path.realpath(__file__)) _ROOT = os.path.dirname(os.path.realpath(__file__))
_VENDOR = os.path.join(_ROOT, "_vendor") _VENDOR = os.path.join(_ROOT, "_vendor")
_CURRENT_VENDOR = os.path.join(
_VENDOR, "py{}".format(".".join(str(v) for v in sys.version_info[:2]))
)
# Add vendored dependencies to path. # Add vendored dependencies to path.
sys.path.insert(0, _VENDOR) sys.path.insert(0, _CURRENT_VENDOR)
from .__version__ import __version__ # noqa from .__version__ import __version__ # noqa
import hashlib
import os import os
import shutil import shutil
import subprocess import subprocess
import sys import sys
import tarfile
from email.parser import Parser
from functools import cmp_to_key from functools import cmp_to_key
from gzip import GzipFile
try:
from urllib.error import HTTPError
from urllib.request import urlopen
except ImportError:
from urllib2 import HTTPError
from urllib2 import urlopen
from ..command import Command from ..command import Command
...@@ -19,12 +27,41 @@ class SelfUpdateCommand(Command): ...@@ -19,12 +27,41 @@ class SelfUpdateCommand(Command):
{ --preview : Install prereleases. } { --preview : Install prereleases. }
""" """
BASE_URL = "https://poetry.eustace.io"
@property
def home(self):
from poetry.utils._compat import Path
from poetry.utils.appdirs import expanduser
home = Path(expanduser("~"))
return home / ".poetry"
@property
def lib(self):
return self.home / "lib"
@property
def lib_backup(self):
return self.home / "lib-backup"
def handle(self): def handle(self):
from poetry.__version__ import __version__ from poetry.__version__ import __version__
from poetry.repositories.pypi_repository import PyPiRepository from poetry.repositories.pypi_repository import PyPiRepository
from poetry.semver import Version from poetry.semver import Version
from poetry.utils._compat import Path
from poetry.utils._compat import decode from poetry.utils._compat import decode
current = Path(__file__)
try:
current.relative_to(self.home)
except ValueError:
raise RuntimeError(
"Poetry was not installed with the recommended installer. "
"Cannot update automatically."
)
version = self.argument("version") version = self.argument("version")
if not version: if not version:
version = ">=" + __version__ version = ">=" + __version__
...@@ -83,98 +120,103 @@ class SelfUpdateCommand(Command): ...@@ -83,98 +120,103 @@ class SelfUpdateCommand(Command):
return e.returncode return e.returncode
def update(self, release): def update(self, release):
from poetry.utils._compat import Path
from poetry.utils.helpers import temporary_directory
version = release.version version = release.version
self.line("Updating to <info>{}</info>".format(version)) self.line("Updating to <info>{}</info>".format(version))
prefix = sys.prefix if self.lib_backup.exists():
base_prefix = getattr(sys, "base_prefix", None) shutil.rmtree(str(self.lib_backup))
real_prefix = getattr(sys, "real_prefix", None)
prefix_poetry = self._bin_path(Path(prefix), "poetry")
if prefix_poetry.exists():
pip = self._bin_path(prefix_poetry.parent.parent, "pip").resolve()
elif (
base_prefix
and base_prefix != prefix
and self._bin_path(Path(base_prefix), "poetry").exists()
):
pip = self._bin_path(Path(base_prefix), "pip")
elif real_prefix:
pip = self._bin_path(Path(real_prefix), "pip")
else:
pip = self._bin_path(Path(prefix), "pip")
if not pip.exists():
raise RuntimeError("Unable to determine poetry's path")
with temporary_directory(prefix="poetry-update-") as temp_dir:
temp_dir = Path(temp_dir)
dist = temp_dir / "dist"
self.line(" - Getting dependencies")
self.process(
str(pip),
"install",
"-U",
"poetry=={}".format(release.version),
"--target",
str(dist),
)
self.line(" - Vendorizing dependencies")
poetry_dir = dist / "poetry"
vendor_dir = poetry_dir / "_vendor"
# Everything, except poetry itself, should
# be put in the _vendor directory
for file in dist.glob("*"):
if file.name.startswith("poetry"):
continue
dest = vendor_dir / file.name # Backup the current installation
if file.is_dir(): if self.lib.exists():
shutil.copytree(str(file), str(dest)) shutil.copytree(str(self.lib), str(self.lib_backup))
shutil.rmtree(str(file)) shutil.rmtree(str(self.lib))
else:
shutil.copy(str(file), str(dest))
os.unlink(str(file))
wheel_data = dist / "poetry-{}.dist-info".format(version) / "WHEEL" try:
with wheel_data.open() as f: self._update(version)
wheel_data = Parser().parsestr(f.read()) except Exception:
if not self.lib_backup.exists():
raise
shutil.copytree(str(self.lib_backup), str(self.lib))
shutil.rmtree(str(self.lib_backup))
raise
finally:
if self.lib_backup.exists():
shutil.rmtree(str(self.lib_backup))
self.line("")
self.line(
"<info>Poetry</info> (<comment>{}</comment>) is installed now. Great!".format(
version
)
)
tag = wheel_data["Tag"] def _update(self, version):
from poetry.utils.helpers import temporary_directory
# Repack everything and install checksum = "poetry-{}-{}.sha256sum".format(version, sys.platform)
self.line(" - Updating <info>poetry</info>")
shutil.make_archive( try:
str(temp_dir / "poetry-{}-{}".format(version, tag)), r = urlopen(self.BASE_URL + "/releases/{}".format(checksum))
format="zip", except HTTPError as e:
root_dir=str(dist), if e.code == 404:
) raise RuntimeError("Could not find {} file".format(checksum))
os.rename( raise
str(temp_dir / "poetry-{}-{}.zip".format(version, tag)),
str(temp_dir / "poetry-{}-{}.whl".format(version, tag)),
)
self.process( checksum = r.read().decode()
str(pip),
"install",
"--upgrade",
"--no-deps",
str(temp_dir / "poetry-{}-{}.whl".format(version, tag)),
)
self.line("") # We get the payload from the remote host
self.line( name = "poetry-{}-{}.tar.gz".format(version, sys.platform)
"<info>poetry</> (<comment>{}</>) " try:
"successfully installed!".format(version) r = urlopen(self.BASE_URL + "/releases/{}".format(name))
) except HTTPError as e:
if e.code == 404:
raise RuntimeError("Could not find {} file".format(name))
raise
meta = r.info()
size = int(meta["Content-Length"])
current = 0
block_size = 8192
bar = self.progress_bar(max=size)
bar.set_format(" - Downloading <info>{}</> <comment>%percent%%</>".format(name))
bar.start()
sha = hashlib.sha256()
with temporary_directory(prefix="poetry-updater-") as dir_:
tar = os.path.join(dir_, name)
with open(tar, "wb") as f:
while True:
buffer = r.read(block_size)
if not buffer:
break
current += len(buffer)
f.write(buffer)
sha.update(buffer)
bar.set_progress(current)
bar.finish()
# Checking hashes
if checksum != sha.hexdigest():
raise RuntimeError(
"Hashes for {} do not match: {} != {}".format(
name, checksum, sha.hexdigest()
)
)
gz = GzipFile(tar, mode="rb")
try:
with tarfile.TarFile(tar, fileobj=gz, format=tarfile.PAX_FORMAT) as f:
f.extractall(str(self.lib))
finally:
gz.close()
def process(self, *args): def process(self, *args):
return subprocess.check_output(list(args), stderr=subprocess.STDOUT) return subprocess.check_output(list(args), stderr=subprocess.STDOUT)
......
[[package]]
category = "dev"
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
name = "appdirs"
optional = false
platform = "*"
python-versions = "*"
version = "1.4.3"
[package.requirements]
python = ">=3.6,<4.0"
[[package]]
category = "dev"
description = "A few extensions to pyyaml."
name = "aspy.yaml"
optional = false
platform = "all"
python-versions = "*"
version = "1.1.1"
[package.dependencies]
pyyaml = "*"
[[package]]
category = "dev"
description = "Atomic file writes."
name = "atomicwrites"
optional = false
platform = "*"
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "1.2.1"
[[package]]
category = "dev"
description = "Classes Without Boilerplate"
name = "attrs"
optional = false
platform = "*"
python-versions = "*"
version = "18.2.0"
[[package]]
category = "dev"
description = "The uncompromising code formatter."
name = "black"
optional = false
platform = "*"
python-versions = ">=3.6"
version = "18.6b4"
[package.dependencies]
appdirs = "*"
attrs = ">=17.4.0"
click = ">=6.5"
toml = ">=0.9.4"
[package.requirements]
python = ">=3.6,<4.0"
[[package]]
category = "main"
description = "httplib2 caching for requests"
name = "cachecontrol"
optional = false
platform = "*"
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.12.5"
[package.dependencies]
lockfile = ">=0.9"
msgpack = "*"
requests = "*"
[[package]]
category = "dev"
description = "A decorator for caching properties in classes."
name = "cached-property"
optional = false
platform = "*"
python-versions = "*"
version = "1.4.3"
[[package]]
category = "main"
description = "Cachy provides a simple yet effective caching library."
name = "cachy"
optional = false
platform = "*"
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.2.0"
[[package]]
category = "main"
description = "Python package for providing Mozilla's CA Bundle."
name = "certifi"
optional = false
platform = "*"
python-versions = "*"
version = "2018.8.24"
[[package]]
category = "dev"
description = "Validate configuration and produce human readable error messages."
name = "cfgv"
optional = false
platform = "*"
python-versions = "*"
version = "1.1.0"
[package.dependencies]
six = "*"
[[package]]
category = "main"
description = "Universal encoding detector for Python 2 and 3"
name = "chardet"
optional = false
platform = "*"
python-versions = "*"
version = "3.0.4"
[[package]]
category = "main"
description = "Cleo allows you to create beautiful and testable command-line interfaces."
name = "cleo"
optional = false
platform = "*"
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.6.8"
[package.dependencies]
pastel = ">=0.1.0,<0.2.0"
pylev = ">=1.3,<2.0"
[[package]]
category = "dev"
description = "A simple wrapper around optparse for powerful command line utilities."
name = "click"
optional = false
platform = "*"
python-versions = "*"
version = "6.7"
[[package]]
category = "dev"
description = "Cross-platform colored terminal text."
name = "colorama"
optional = false
platform = "UNKNOWN"
python-versions = "*"
version = "0.3.9"
[package.requirements]
platform = "win32"
[[package]]
category = "dev"
description = "Code coverage measurement for Python"
name = "coverage"
optional = false
platform = "*"
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4"
version = "4.5.1"
[[package]]
category = "main"
description = "Python 3.4 Enum backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4"
name = "enum34"
optional = false
platform = "UNKNOWN"
python-versions = "*"
version = "1.1.6"
[package.requirements]
python = ">=2.7,<2.8"
[[package]]
category = "dev"
description = "Python function signatures from PEP362 for Python 2.6, 2.7 and 3.2+"
name = "funcsigs"
optional = false
platform = "UNKNOWN"
python-versions = "*"
version = "1.0.2"
[package.requirements]
python = "<3.0"
[[package]]
category = "main"
description = "Backport of the functools module from Python 3.2.3 for use on 2.7 and PyPy."
name = "functools32"
optional = false
platform = "UNKNOWN"
python-versions = "*"
version = "3.2.3-2"
[package.requirements]
python = ">=2.7,<2.8"
[[package]]
category = "main"
description = "HTML parser based on the WHATWG HTML specification"
name = "html5lib"
optional = false
platform = "*"
python-versions = "*"
version = "1.0.1"
[package.dependencies]
six = ">=1.9"
webencodings = "*"
[[package]]
category = "dev"
description = "File identification library for Python"
name = "identify"
optional = false
platform = "*"
python-versions = "*"
version = "1.1.4"
[[package]]
category = "main"
description = "Internationalized Domain Names in Applications (IDNA)"
name = "idna"
optional = false
platform = "*"
python-versions = "*"
version = "2.7"
[[package]]
category = "dev"
description = "A small but fast and easy to use stand-alone template engine written in pure python."
name = "jinja2"
optional = false
platform = "*"
python-versions = "*"
version = "2.10"
[package.dependencies]
MarkupSafe = ">=0.23"
[[package]]
category = "main"
description = "An implementation of JSON Schema validation for Python"
name = "jsonschema"
optional = false
platform = "*"
python-versions = "*"
version = "2.6.0"
[package.dependencies]
[package.dependencies.functools32]
python = ">=2.7,<2.8"
version = "*"
[[package]]
category = "dev"
description = "Python LiveReload is an awesome tool for web developers"
name = "livereload"
optional = false
platform = "*"
python-versions = "*"
version = "2.5.2"
[package.dependencies]
six = "*"
tornado = "*"
[[package]]
category = "main"
description = "Platform-independent file locking module"
name = "lockfile"
optional = false
platform = "UNKNOWN"
python-versions = "*"
version = "0.12.2"
[[package]]
category = "dev"
description = "Python implementation of Markdown."
name = "markdown"
optional = false
platform = "*"
python-versions = "*"
version = "2.6.11"
[[package]]
category = "dev"
description = "Implements a XML/HTML/XHTML Markup safe string for Python"
name = "markupsafe"
optional = false
platform = "UNKNOWN"
python-versions = "*"
version = "1.0"
[[package]]
category = "dev"
description = "Project documentation with Markdown."
name = "mkdocs"
optional = false
platform = "*"
python-versions = ">=2.7.9,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
version = "1.0.3"
[package.dependencies]
Jinja2 = ">=2.7.1"
Markdown = ">=2.3.1"
PyYAML = ">=3.10"
click = ">=3.3"
livereload = ">=2.5.1"
tornado = ">=5.0"
[[package]]
category = "dev"
description = "Rolling backport of unittest.mock for all Pythons"
name = "mock"
optional = false
platform = "UNKNOWN"
python-versions = "*"
version = "2.0.0"
[package.dependencies]
pbr = ">=0.11"
six = ">=1.9"
[package.dependencies.funcsigs]
python = "<3.3"
version = ">=1"
[package.requirements]
python = "<3.0"
[[package]]
category = "dev"
description = "More routines for operating on iterables, beyond itertools"
name = "more-itertools"
optional = false
platform = "*"
python-versions = "*"
version = "4.3.0"
[package.dependencies]
six = ">=1.0.0,<2.0.0"
[[package]]
category = "main"
description = "MessagePack (de)serializer."
name = "msgpack"
optional = false
platform = "*"
python-versions = "*"
version = "0.5.6"
[[package]]
category = "dev"
description = "Node.js virtual environment builder"
name = "nodeenv"
optional = false
platform = "any"
python-versions = "*"
version = "1.3.2"
[[package]]
category = "main"
description = "Bring colors to your terminal."
name = "pastel"
optional = false
platform = "UNKNOWN"
python-versions = "*"
version = "0.1.0"
[[package]]
category = "main"
description = "Object-oriented filesystem paths"
name = "pathlib2"
optional = false
platform = "*"
python-versions = "*"
version = "2.3.2"
[package.dependencies]
six = "*"
[package.dependencies.scandir]
python = "<3.5"
version = "*"
[package.requirements]
python = "<3.6"
[[package]]
category = "dev"
description = "Python Build Reasonableness"
name = "pbr"
optional = false
platform = "*"
python-versions = "*"
version = "4.2.0"
[package.requirements]
python = "<3.0"
[[package]]
category = "main"
description = "Query metadatdata from sdists / bdists / installed packages."
name = "pkginfo"
optional = false
platform = "Unix"
python-versions = "*"
version = "1.4.2"
[[package]]
category = "dev"
description = "plugin and hook calling mechanisms for python"
name = "pluggy"
optional = false
platform = "unix"
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.7.1"
[[package]]
category = "dev"
description = "A framework for managing and maintaining multi-language pre-commit hooks."
name = "pre-commit"
optional = false
platform = "*"
python-versions = "*"
version = "1.11.0"
[package.dependencies]
"aspy.yaml" = "*"
cached-property = "*"
cfgv = ">=1.0.0"
identify = ">=1.0.0"
nodeenv = ">=0.11.1"
pyyaml = "*"
six = "*"
toml = "*"
virtualenv = "*"
[[package]]
category = "dev"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
name = "py"
optional = false
platform = "unix"
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "1.6.0"
[[package]]
category = "dev"
description = "Pygments is a syntax highlighting package written in Python."
name = "pygments"
optional = false
platform = "any"
python-versions = "*"
version = "2.2.0"
[[package]]
category = "dev"
description = "Pygments Github custom lexers."
name = "pygments-github-lexers"
optional = false
platform = "UNKNOWN"
python-versions = "*"
version = "0.0.5"
[package.dependencies]
pygments = ">=2.0.2"
[[package]]
category = "main"
description = "A pure Python Levenshtein implementation that's not freaking GPL'd."
name = "pylev"
optional = false
platform = "UNKNOWN"
python-versions = "*"
version = "1.3.0"
[[package]]
category = "dev"
description = "Extension pack for Python Markdown."
name = "pymdown-extensions"
optional = false
platform = "*"
python-versions = "*"
version = "4.12"
[package.dependencies]
Markdown = ">=2.6.10"
[[package]]
category = "main"
description = "Python parsing module"
name = "pyparsing"
optional = false
platform = "UNKNOWN"
python-versions = "*"
version = "2.2.0"
[[package]]
category = "main"
description = "Persistent/Functional/Immutable data structures"
name = "pyrsistent"
optional = false
platform = "*"
python-versions = "*"
version = "0.14.4"
[package.dependencies]
six = "*"
[[package]]
category = "dev"
description = "pytest: simple powerful testing with Python"
name = "pytest"
optional = false
platform = "unix"
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "3.7.4"
[package.dependencies]
atomicwrites = ">=1.0"
attrs = ">=17.4.0"
more-itertools = ">=4.0.0"
pluggy = ">=0.7"
py = ">=1.5.0"
setuptools = "*"
six = ">=1.10.0"
[package.dependencies.colorama]
platform = "win32"
version = "*"
[package.dependencies.funcsigs]
python = "<3.0"
version = "*"
[package.dependencies.pathlib2]
python = "<3.6"
version = ">=2.2.0"
[[package]]
category = "dev"
description = "Pytest plugin for measuring coverage."
name = "pytest-cov"
optional = false
platform = "*"
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "2.6.0"
[package.dependencies]
coverage = ">=4.4"
pytest = ">=2.9"
[[package]]
category = "dev"
description = "Thin-wrapper around the mock package for easier use with py.test"
name = "pytest-mock"
optional = false
platform = "any"
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "1.10.0"
[package.dependencies]
pytest = ">=2.7"
[package.dependencies.mock]
python = "<3.0"
version = "*"
[[package]]
category = "dev"
description = "YAML parser and emitter for Python"
name = "pyyaml"
optional = false
platform = "Any"
python-versions = "*"
version = "3.13"
[[package]]
category = "main"
description = "Python HTTP for Humans."
name = "requests"
optional = false
platform = "*"
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "2.19.1"
[package.dependencies]
certifi = ">=2017.4.17"
chardet = ">=3.0.2,<3.1.0"
idna = ">=2.5,<2.8"
urllib3 = ">=1.21.1,<1.24"
[[package]]
category = "main"
description = "A utility belt for advanced users of python-requests"
name = "requests-toolbelt"
optional = false
platform = "*"
python-versions = "*"
version = "0.8.0"
[package.dependencies]
requests = ">=2.0.1,<3.0.0"
[[package]]
category = "main"
description = "scandir, a better directory iterator and faster os.walk()"
name = "scandir"
optional = false
platform = "*"
python-versions = "*"
version = "1.9.0"
[package.requirements]
python = "<3.5"
[[package]]
category = "main"
description = "Tool to Detect Surrounding Shell"
name = "shellingham"
optional = false
platform = "*"
python-versions = ">=2.6,!=3.0,!=3.1,!=3.2,!=3.3"
version = "1.2.4"
[[package]]
category = "main"
description = "Python 2 and 3 compatibility utilities"
name = "six"
optional = false
platform = "*"
python-versions = "*"
version = "1.11.0"
[[package]]
category = "dev"
description = "Python Library for Tom's Obvious, Minimal Language"
name = "toml"
optional = false
platform = "*"
python-versions = "*"
version = "0.9.4"
[[package]]
category = "main"
description = "Style preserving TOML library"
name = "tomlkit"
optional = false
platform = "*"
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.4.4"
[package.dependencies]
[package.dependencies.enum34]
python = ">=2.7,<2.8"
version = ">=1.1,<2.0"
[package.dependencies.typing]
python = ">=2.7,<2.8 || >=3.4,<3.5"
version = ">=3.6,<4.0"
[[package]]
category = "dev"
description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed."
name = "tornado"
optional = false
platform = "*"
python-versions = ">= 2.7, !=3.0.*, !=3.1.*, !=3.2.*, != 3.3.*"
version = "5.1"
[[package]]
category = "dev"
description = "virtualenv-based automation of test activities"
name = "tox"
optional = false
platform = "unix"
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "3.2.1"
[package.dependencies]
pluggy = ">=0.3.0,<1"
py = ">=1.4.17,<2"
setuptools = ">=30.0.0"
six = ">=1.0.0,<2"
virtualenv = ">=1.11.2"
[[package]]
category = "main"
description = "Type Hints for Python"
name = "typing"
optional = false
platform = "*"
python-versions = "*"
version = "3.6.6"
[package.requirements]
python = ">=2.7,<2.8 || >=3.4,<3.5"
[[package]]
category = "main"
description = "HTTP library with thread-safe connection pooling, file post, and more."
name = "urllib3"
optional = false
platform = "*"
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4"
version = "1.23"
[[package]]
category = "main"
description = "Virtual Python Environment builder"
name = "virtualenv"
optional = false
platform = "*"
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*"
version = "16.0.0"
[[package]]
category = "main"
description = "Character encoding aliases for legacy web content"
name = "webencodings"
optional = false
platform = "*"
python-versions = "*"
version = "0.5.1"
[metadata]
content-hash = "5c5e11cb29f8ef5ddf0c3bf98c849597799c71d99cd78157807f439a788036e7"
platform = "*"
python-versions = "~2.7 || ^3.4"
[metadata.hashes]
appdirs = ["9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", "d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"]
"aspy.yaml" = ["04d26279513618f1024e1aba46471db870b3b33aef204c2d09bcf93bea9ba13f", "0a77e23fafe7b242068ffc0252cee130d3e509040908fc678d9d1060e7494baa"]
atomicwrites = ["0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0", "ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee"]
attrs = ["10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69", "ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb"]
black = ["22158b89c1a6b4eb333a1e65e791a3f8b998cf3b11ae094adb2570f31f769a44", "4b475bbd528acce094c503a3d2dbc2d05a4075f6d0ef7d9e7514518e14cc5191"]
cachecontrol = ["cef77effdf51b43178f6a2d3b787e3734f98ade253fa3187f3bb7315aaa42ff7"]
cached-property = ["630fdbf0f4ac7d371aa866016eba1c3ac43e9032246748d4994e67cb05f99bc4", "f1f9028757dc40b4cb0fd2234bd7b61a302d7b84c683cb8c2c529238a24b8938"]
cachy = ["b71513e5a38ce90c1280c02b7d8d6bb3fdf64666c9cc0584f2479afea097d56c", "b71e8e7ddb5b386e23e81befdfac8a93885406139b8681bedc17b3444fcb8fca"]
certifi = ["376690d6f16d32f9d1fe8932551d80b23e9d393a8578c5633a2ed39a64861638", "456048c7e371c089d0a77a5212fb37a2c2dce1e24146e3b7e0261736aaeaa22a"]
cfgv = ["73f48a752bd7aab103c4b882d6596c6360b7aa63b34073dd2c35c7b4b8f93010", "d1791caa9ff5c0c7bce80e7ecc1921752a2eb7c2463a08ed9b6c96b85a2f75aa"]
chardet = ["84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", "fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"]
cleo = ["85a63076b72ca376fb06668be1fc7758dc16740b394783d5cc65200c4b32f71b", "9b7f79f1aa470a025c0d28c76aa225ee9e65028d32f80032e871aa3500df61b8"]
click = ["29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d", "f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b"]
colorama = ["463f8483208e921368c9f306094eb6f725c6ca42b0f97e313cb5d5512459feda", "48eb22f4f8461b1df5734a074b57042430fb06e1d61bd1e11b078c0fe6d7a1f1"]
coverage = ["03481e81d558d30d230bc12999e3edffe392d244349a90f4ef9b88425fac74ba", "0b136648de27201056c1869a6c0d4e23f464750fd9a9ba9750b8336a244429ed", "104ab3934abaf5be871a583541e8829d6c19ce7bde2923b2751e0d3ca44db60a", "10a46017fef60e16694a30627319f38a2b9b52e90182dddb6e37dcdab0f4bf95", "15b111b6a0f46ee1a485414a52a7ad1d703bdf984e9ed3c288a4414d3871dcbd", "198626739a79b09fa0a2f06e083ffd12eb55449b5f8bfdbeed1df4910b2ca640", "1c383d2ef13ade2acc636556fd544dba6e14fa30755f26812f54300e401f98f2", "23d341cdd4a0371820eb2b0bd6b88f5003a7438bbedb33688cd33b8eae59affd", "28b2191e7283f4f3568962e373b47ef7f0392993bb6660d079c62bd50fe9d162", "2a5b73210bad5279ddb558d9a2bfedc7f4bf6ad7f3c988641d83c40293deaec1", "2eb564bbf7816a9d68dd3369a510be3327f1c618d2357fa6b1216994c2e3d508", "337ded681dd2ef9ca04ef5d93cfc87e52e09db2594c296b4a0a3662cb1b41249", "3a2184c6d797a125dca8367878d3b9a178b6fdd05fdc2d35d758c3006a1cd694", "3c79a6f7b95751cdebcd9037e4d06f8d5a9b60e4ed0cd231342aa8ad7124882a", "3d72c20bd105022d29b14a7d628462ebdc61de2f303322c0212a054352f3b287", "3eb42bf89a6be7deb64116dd1cc4b08171734d721e7a7e57ad64cc4ef29ed2f1", "4635a184d0bbe537aa185a34193898eee409332a8ccb27eea36f262566585000", "56e448f051a201c5ebbaa86a5efd0ca90d327204d8b059ab25ad0f35fbfd79f1", "5a13ea7911ff5e1796b6d5e4fbbf6952381a611209b736d48e675c2756f3f74e", "69bf008a06b76619d3c3f3b1983f5145c75a305a0fea513aca094cae5c40a8f5", "6bc583dc18d5979dc0f6cec26a8603129de0304d5ae1f17e57a12834e7235062", "701cd6093d63e6b8ad7009d8a92425428bc4d6e7ab8d75efbb665c806c1d79ba", "7608a3dd5d73cb06c531b8925e0ef8d3de31fed2544a7de6c63960a1e73ea4bc", "76ecd006d1d8f739430ec50cc872889af1f9c1b6b8f48e29941814b09b0fd3cc", "7aa36d2b844a3e4a4b356708d79fd2c260281a7390d678a10b91ca595ddc9e99", "7d3f553904b0c5c016d1dad058a7554c7ac4c91a789fca496e7d8347ad040653", "7e1fe19bd6dce69d9fd159d8e4a80a8f52101380d5d3a4d374b6d3eae0e5de9c", "8c3cb8c35ec4d9506979b4cf90ee9918bc2e49f84189d9bf5c36c0c1119c6558", "9d6dd10d49e01571bf6e147d3b505141ffc093a06756c60b053a859cb2128b1f", "9e112fcbe0148a6fa4f0a02e8d58e94470fc6cb82a5481618fea901699bf34c4", "ac4fef68da01116a5c117eba4dd46f2e06847a497de5ed1d64bb99a5fda1ef91", "b8815995e050764c8610dbc82641807d196927c3dbed207f0a079833ffcf588d", "be6cfcd8053d13f5f5eeb284aa8a814220c3da1b0078fa859011c7fffd86dab9", "c1bb572fab8208c400adaf06a8133ac0712179a334c09224fb11393e920abcdd", "de4418dadaa1c01d497e539210cb6baa015965526ff5afc078c57ca69160108d", "e05cb4d9aad6233d67e0541caa7e511fa4047ed7750ec2510d466e806e0255d6", "e4d96c07229f58cb686120f168276e434660e4358cc9cf3b0464210b04913e77", "f3f501f345f24383c0000395b26b726e46758b71393267aeae0bd36f8b3ade80", "f8a923a85cb099422ad5a2e345fe877bbc89a8a8b23235824a93488150e45f6e"]
enum34 = ["2d81cbbe0e73112bdfe6ef8576f2238f2ba27dd0d55752a776c41d38b7da2850", "644837f692e5f550741432dd3f223bbb9852018674981b1664e5dc339387588a", "6bd0f6ad48ec2aa117d3d141940d484deccda84d4fcd884f5c3d93c23ecd8c79", "8ad8c4783bf61ded74527bffb48ed9b54166685e4230386a9ed9b1279e2df5b1"]
funcsigs = ["330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca", "a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50"]
functools32 = ["89d824aa6c358c421a234d7f9ee0bd75933a67c29588ce50aaa3acdf4d403fa0", "f6253dfbe0538ad2e387bd8fdfd9293c925d63553f5813c4e587745416501e6d"]
html5lib = ["20b159aa3badc9d5ee8f5c647e5efd02ed2a66ab8d354930bd9ff139fc1dc0a3", "66cb0dcfdbbc4f9c3ba1a63fdb511ffdbd4f513b2b6d81b80cd26ce6b3fb3736"]
identify = ["49845e70fc6b1ec3694ab930a2c558912d7de24548eebcd448f65567dc757c43", "68daab16a3db364fa204591f97dc40bfffd1a7739f27788a4895b4d8fd3516e5"]
idna = ["156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e", "684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16"]
jinja2 = ["74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd", "f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"]
jsonschema = ["000e68abd33c972a5248544925a0cae7d1125f9bf6c58280d37546b946769a08", "6ff5f3180870836cae40f06fa10419f557208175f13ad7bc26caa77beb1f6e02"]
livereload = ["583179dc8d49b040a9da79bd33de59e160d2a8802b939e304eb359a4419f6498", "dd4469a8f5a6833576e9f5433f1439c306de15dbbfeceabd32479b1123380fa5"]
lockfile = ["6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799", "6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa"]
markdown = ["9ba587db9daee7ec761cfc656272be6aabe2ed300fece21208e4aab2e457bc8f", "a856869c7ff079ad84a3e19cd87a64998350c2b94e9e08e44270faef33400f81"]
markupsafe = ["a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665"]
mkdocs = ["50bf14ba9fdef391ae3e416ce66f1c598949d6e9cf2e9d7ed69abe3cf368539a", "ce8c238231d8df02e9f66390b5ff153f52682326d83cac412d7f44957b704fbb"]
mock = ["5ce3c71c5545b472da17b72268978914d0252980348636840bd34a00b5cc96c1", "b158b6df76edd239b8208d481dc46b6afd45a846b7812ff0ce58971cf5bc8bba"]
more-itertools = ["c187a73da93e7a8acc0001572aebc7e3c69daf7bf6881a2cea10650bd4420092", "c476b5d3a34e12d40130bc2f935028b5f636df8f372dc2c1c01dc19681b2039e", "fcbfeaea0be121980e15bc97b3817b5202ca73d0eae185b4550cbfce2a3ebb3d"]
msgpack = ["0b3b1773d2693c70598585a34ca2715873ba899565f0a7c9a1545baef7e7fbdc", "0bae5d1538c5c6a75642f75a1781f3ac2275d744a92af1a453c150da3446138b", "0ee8c8c85aa651be3aa0cd005b5931769eaa658c948ce79428766f1bd46ae2c3", "1369f9edba9500c7a6489b70fdfac773e925342f4531f1e3d4c20ac3173b1ae0", "22d9c929d1d539f37da3d1b0e16270fa9d46107beab8c0d4d2bddffffe895cee", "2ff43e3247a1e11d544017bb26f580a68306cec7a6257d8818893c1fda665f42", "31a98047355d34d047fcdb55b09cb19f633cf214c705a765bd745456c142130c", "8767eb0032732c3a0da92cbec5ac186ef89a3258c6edca09161472ca0206c45f", "8acc8910218555044e23826980b950e96685dc48124a290c86f6f41a296ea172", "ab189a6365be1860a5ecf8159c248f12d33f79ea799ae9695fa6a29896dcf1d4", "cfd6535feb0f1cf1c7cdb25773e965cc9f92928244a8c3ef6f8f8a8e1f7ae5c4", "e274cd4480d8c76ec467a85a9c6635bbf2258f0649040560382ab58cabb44bcf", "f86642d60dca13e93260187d56c2bef2487aa4d574a669e8ceefcf9f4c26fd00", "f8a57cbda46a94ed0db55b73e6ab0c15e78b4ede8690fa491a0e55128d552bb0", "fcea97a352416afcbccd7af9625159d80704a25c519c251c734527329bb20d0e"]
nodeenv = ["aa040ab5189bae17d272175609010be6c5b589ec4b8dbd832cc50c9e9cb7496f"]
pastel = ["3108af417ec0fa6d0a620e676ec4f02c839ca13e10611586e5d2174b46aa0bc3", "d1fee8079534f99f1805a044fef946d23eee6d6a7cd34292c30e6c16be9a80b9"]
pathlib2 = ["8eb170f8d0d61825e09a95b38be068299ddeda82f35e96c3301a8a5e7604cb83", "d1aa2a11ba7b8f7b21ab852b1fb5afb277e1bb99d5dfc663380b5015c0d80c5a"]
pbr = ["1b8be50d938c9bb75d0eaf7eda111eec1bf6dc88a62a6412e33bf077457e0f45", "b486975c0cafb6beeb50ca0e17ba047647f229087bd74e37f4a7e2cac17d2caa"]
pkginfo = ["5878d542a4b3f237e359926384f1dde4e099c9f5525d236b1840cf704fa8d474", "a39076cb3eb34c333a0dd390b568e9e1e881c7bf2cc0aee12120636816f55aee"]
pluggy = ["6e3836e39f4d36ae72840833db137f7b7d35105079aee6ec4a62d9f80d594dd1", "95eb8364a4708392bae89035f45341871286a333f749c3141c20573d2b3876e1"]
pre-commit = ["18ef2892ff6b6518945bc7dcf423e3c969033a4ca132b6a1ae0d52eb2e51ea27", "88d59872610a7069d937b6868632ba534187bda58c4665de12b25c8c549ddd0e"]
py = ["06a30435d058473046be836d3fc4f27167fd84c45b99704f2fb5509ef61f9af1", "50402e9d1c9005d759426988a492e0edaadb7f4e68bcddfea586bc7432d009c6"]
pygments = ["78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d", "dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc"]
pygments-github-lexers = ["0f9e9fb607d351c127a1e55e82a6eb491ed1fc11b2d6a0444ba217dc6d1f82c1", "aaca57e77cd6fcfce8d6ee97a998962eebf7fbb810519a8ebde427c62823e133"]
pylev = ["063910098161199b81e453025653ec53556c1be7165a9b7c50be2f4d57eae1c3", "1d29a87beb45ebe1e821e7a3b10da2b6b2f4c79b43f482c2df1a1f748a6e114e"]
pymdown-extensions = ["20f2ae1067ab850cab92fcf57487267a7fd1365a7b1e7c5394e1e0778455eec1", "7d3fcbb4c5d70a78d1f4c2c7eef02dbe7e1ba08b06cb72e08b3d1027eb77458b"]
pyparsing = ["0832bcf47acd283788593e7a0f542407bd9550a55a8a8435214a1960e04bcb04", "281683241b25fe9b80ec9d66017485f6deff1af5cde372469134b56ca8447a07", "8f1e18d3fd36c6795bb7e02a39fd05c611ffc2596c1e0d995d34d67630426c18", "9e8143a3e15c13713506886badd96ca4b579a87fbdf49e550dbfc057d6cb218e", "b8b3117ed9bdf45e14dcc89345ce638ec7e0e29b2b579fa1ecf32ce45ebac8a5", "e4d45427c6e20a59bf4f88c639dcc03ce30d193112047f94012102f235853a58", "fee43f17a9c4087e7ed1605bd6df994c6173c1e977d7ade7b651292fab2bd010"]
pyrsistent = ["4024f838472cba9ea1ccbc638e0bcafec2efda28594a9905177ec365f1a95fea"]
pytest = ["2d7c49e931316cc7d1638a3e5f54f5d7b4e5225972b3c9838f3584788d27f349", "ad0c7db7b5d4081631e0155f5c61b80ad76ce148551aaafe3a718d65a7508b18"]
pytest-cov = ["513c425e931a0344944f84ea47f3956be0e416d95acbd897a44970c8d926d5d7", "e360f048b7dae3f2f2a9a4d067b2dd6b6a015d384d1577c994a43f3f7cbad762"]
pytest-mock = ["53801e621223d34724926a5c98bd90e8e417ce35264365d39d6c896388dcc928", "d89a8209d722b8307b5e351496830d5cc5e192336003a485443ae9adeb7dd4c0"]
pyyaml = ["3d7da3009c0f3e783b2c873687652d83b1bbfd5c88e9813fb7e5b03c0dd3108b", "3ef3092145e9b70e3ddd2c7ad59bdd0252a94dfe3949721633e41344de00a6bf", "40c71b8e076d0550b2e6380bada1f1cd1017b882f7e16f09a65be98e017f211a", "558dd60b890ba8fd982e05941927a3911dc409a63dcb8b634feaa0cda69330d3", "a7c28b45d9f99102fa092bb213aa12e0aaf9a6a1f5e395d36166639c1f96c3a1", "aa7dd4a6a427aed7df6fb7f08a580d68d9b118d90310374716ae90b710280af1", "bc558586e6045763782014934bfaf39d48b8ae85a2713117d16c39864085c613", "d46d7982b62e0729ad0175a9bc7e10a566fc07b224d2c79fafb5e032727eaa04", "d5eef459e30b09f5a098b9cea68bebfeb268697f78d647bd255a085371ac7f3f", "e01d3203230e1786cd91ccfdc8f8454c8069c91bee3962ad93b87a4b2860f537", "e170a9e6fcfd19021dd29845af83bb79236068bf5fd4df3327c1be18182b2531"]
requests = ["63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1", "ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a"]
requests-toolbelt = ["42c9c170abc2cacb78b8ab23ac957945c7716249206f90874651971a4acff237", "f6a531936c6fa4c6cfce1b9c10d5c4f498d16528d2a54a22ca00011205a187b5"]
scandir = ["04b8adb105f2ed313a7c2ef0f1cf7aff4871aa7a1883fa4d8c44b5551ab052d6", "1444134990356c81d12f30e4b311379acfbbcd03e0bab591de2696a3b126d58e", "1b5c314e39f596875e5a95dd81af03730b338c277c54a454226978d5ba95dbb6", "346619f72eb0ddc4cf355ceffd225fa52506c92a2ff05318cfabd02a144e7c4e", "44975e209c4827fc18a3486f257154d34ec6eaec0f90fef0cca1caa482db7064", "61859fd7e40b8c71e609c202db5b0c1dbec0d5c7f1449dec2245575bdc866792", "a5e232a0bf188362fa00123cc0bb842d363a292de7126126df5527b6a369586a", "c14701409f311e7a9b7ec8e337f0815baf7ac95776cc78b419a1e6d49889a383", "c7708f29d843fc2764310732e41f0ce27feadde453261859ec0fca7865dfc41b", "c9009c527929f6e25604aec39b0a43c3f831d2947d89d6caaab22f057b7055c8", "f5c71e29b4e2af7ccdc03a020c626ede51da471173b4a6ad1e904f2b2e04b4bd"]
shellingham = ["869c760320b6d4cd88ac5bdc2a6a40c042fb388c5fcd056f8a07a82d9859cc40", "c09c563a2e185ec3d64e43c286dbba3150fc182d96cd29ff5b002f3d3c3f5076"]
six = ["70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", "832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"]
toml = ["8e86bd6ce8cc11b9620cb637466453d94f5d57ad86f17e98a98d1f73e3baab2d"]
tomlkit = ["8ab16e93162fc44d3ad83d2aa29a7140b8f7d996ae1790a73b9a7aed6fb504ac", "ca181cee7aee805d455628f7c94eb8ae814763769a93e69157f250fe4ebe1926"]
tornado = ["1c0816fc32b7d31b98781bd8ebc7a9726d7dce67407dc353a2e66e697e138448", "4f66a2172cb947387193ca4c2c3e19131f1c70fa8be470ddbbd9317fd0801582", "5327ba1a6c694e0149e7d9126426b3704b1d9d520852a3e4aa9fc8fe989e4046", "6a7e8657618268bb007646b9eae7661d0b57f13efc94faa33cd2588eae5912c9", "a9b14804783a1d77c0bd6c66f7a9b1196cbddfbdf8bceb64683c5ae60bd1ec6f", "c58757e37c4a3172949c99099d4d5106e4d7b63aa0617f9bb24bfbff712c7866", "d8984742ce86c0855cccecd5c6f54a9f7532c983947cff06f3a0e2115b47f85c"]
tox = ["37cf240781b662fb790710c6998527e65ca6851eace84d1595ee71f7af4e85f7", "eb61aa5bcce65325538686f09848f04ef679b5cd9b83cc491272099b28739600"]
typing = ["4027c5f6127a6267a435201981ba156de91ad0d1d98e9ddc2aa173453453492d", "57dcf675a99b74d64dacf6fba08fb17cf7e3d5fdff53d4a30ea2a5e7e52543d4", "a4c8473ce11a65999c8f59cb093e70686b6c84c98df58c1dae9b3b196089858a"]
urllib3 = ["a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf", "b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5"]
virtualenv = ["2ce32cd126117ce2c539f0134eb89de91a8413a29baac49cbab3eb50e2026669", "ca07b4c0b54e14a91af9f34d0919790b016923d157afda5efdde55c96718f752"]
webencodings = ["a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", "b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"]
#!/usr/bin/env python
import hashlib
import os
import shutil
import subprocess
import sys
import tarfile
from gzip import GzipFile
from cleo import Application
from cleo import Command
WINDOWS = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt")
class MakeReleaseCommand(Command):
"""
Makes a self-contained package of Poetry.
make:release
{--P|python=?* : Python version to use}
"""
PYTHON = {
"2.7": "python2.7",
"3.4": "python3.4",
"3.5": "python3.5",
"3.6": "python3.6",
"3.7": "python3.7",
}
def handle(self):
pythons = self.PYTHON
if self.option("python"):
pythons = {}
for python in self.option("python"):
parts = python.split(":", 1)
if len(parts) == 1:
pythons[parts[0]] = self.PYTHON[parts[0]]
version, python = parts
pythons[version] = python
self.check_system(pythons)
from poetry import __version__
from poetry.poetry import Poetry
from poetry.puzzle import Solver
from poetry.repositories.pool import Pool
from poetry.repositories.repository import Repository
from poetry.utils._compat import Path
from poetry.utils.helpers import temporary_directory
project = Poetry.create(Path.cwd())
package = project.package
del package.dev_requires[:]
# We only use the lock file to resolve the dependencies
pool = Pool()
pool.add_repository(project.locker.locked_repository(with_dev_reqs=True))
with temporary_directory() as tmp_dir:
# Copy poetry to tmp dir
poetry_dir = os.path.join(tmp_dir, "poetry")
shutil.copytree(
os.path.join(os.path.dirname(__file__), "poetry"), poetry_dir
)
for version, python in sorted(pythons.items()):
self.line(
"<info>Preparing files for Python <comment>{}</comment></info>".format(
version
)
)
with package.with_python_versions("^{}".format(version)):
solver = Solver(
package, pool, Repository(), Repository(), self.output
)
ops = solver.solve()
self.vendorize_for_python(
python, [op.package for op in ops], poetry_dir, version
)
self.line("")
self.line("<info>Packaging files</info>")
with temporary_directory() as tmp_dir2:
base_name = "poetry-{}-{}".format(__version__, sys.platform)
name = "{}.tar.gz".format(base_name)
gz = GzipFile(os.path.join(tmp_dir2, name), mode="wb")
try:
with tarfile.TarFile(
os.path.join(tmp_dir2, name),
mode="w",
fileobj=gz,
format=tarfile.PAX_FORMAT,
) as tar:
for root, dirs, files in os.walk(tmp_dir):
for f in files:
if f.endswith(".pyc"):
continue
path = os.path.join(os.path.realpath(root), f)
relpath = os.path.relpath(
path, os.path.realpath(tmp_dir)
)
tar_info = tar.gettarinfo(str(path), arcname=relpath)
if tar_info.isreg():
with open(path, "rb") as f:
tar.addfile(tar_info, f)
else:
tar.addfile(tar_info) # Symlinks & ?
finally:
gz.close()
releases_dir = os.path.join(os.path.dirname(__file__), "releases")
if not os.path.exists(releases_dir):
os.mkdir(releases_dir)
shutil.copyfile(
os.path.join(tmp_dir2, name), os.path.join(releases_dir, name)
)
# Compute hash
sha = hashlib.sha256()
with open(os.path.join(releases_dir, name), "rb") as f:
while True:
buffer = f.read(8192)
if not buffer:
break
sha.update(buffer)
with open(
os.path.join(releases_dir, "{}.sha256sum".format(base_name)), "w"
) as f:
f.write(sha.hexdigest())
self.line("<info>Built <comment>{}</comment></info>".format(name))
def check_system(self, pythons):
for version, python in sorted(pythons.items()):
try:
subprocess.check_output(
[python, "-V"], stderr=subprocess.STDOUT, shell=WINDOWS
)
except subprocess.CalledProcessError:
raise RuntimeError("Python {} is not available".format(version))
def vendorize_for_python(self, python, packages, dest, python_version):
vendor_dir = os.path.join(dest, "_vendor", "py{}".format(python_version))
bar = self.progress_bar(max=len(packages))
bar.set_format("%message% %current%/%max%")
bar.set_message(
"<info>Vendorizing dependencies for Python <comment>{}</comment></info>".format(
python_version
)
)
bar.start()
for package in packages:
subprocess.check_output(
[
python,
"-m",
"pip",
"install",
package.name,
"--no-deps",
"--target",
vendor_dir,
],
stderr=subprocess.STDOUT,
shell=WINDOWS,
)
bar.advance()
bar.finish()
self.line("")
app = Application("Sonnet")
app.add(MakeReleaseCommand())
if __name__ == "__main__":
app.run()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment