Loading CHANGELOG.md +2 −1 Changes for CHANGELOG.md: 2 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -5,7 +5,8 @@ ### Added - Added compatibility with Python 3.4 and 3.5. - Added the `version` command to automatically bump the packages' version. - Added the `version` command to automatically bump the package's version. - Added a standalone installer to install `poetry` isolated. ### Changed Loading docs/docs/index.md +26 −3 Changes for docs/docs/index.md: 26 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -12,12 +12,35 @@ on Windows, Linux and OSX. ## Installation Installing Poetry is as simple as: Poetry provides a custom installer that will install `poetry` isolated from the rest of your system by vendorizing its dependencies. This is the recommended way of installing `poetry`. ```bash pip install --user poetry 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. 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 python get-poetry.py --version 0.7.0 ``` !!!note In the future, Poetry will come with its standalone installer. Using `pip` to install `poetry` is also possible. ```bash pip install --user poetry ``` Be aware, however, that it will also install poetry's dependencies which might cause conflicts. get-poetry.py 0 → 100644 +259 −0 Changes for get-poetry.py: 259 added lines, 0 removed lines. Original line number Diff line number Diff line """ This script will install poetry and its dependencies in isolation from the rest of the system. It does, in order: - Downloads the latest stable version of poetry. - Checks if the _vendor directory is empty. - If the _vendor directory is not empty, empties it. - Installs all dependencies in the _vendor directory This ensure that poetry will look for its dependencies inside the _vendor directory without tampering with the base system. Note, however, that installing poetry via pip will still work, since if poetry does not find the dependencies in the _vendor directory, it will look for them in the base system. """ import argparse import json import os import re import shutil import subprocess import sys import tempfile from email.parser import Parser from pathlib import Path from urllib.request import urlopen FOREGROUND_COLORS = { 'black': 30, 'red': 31, 'green': 32, 'yellow': 33, 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37 } BACKGROUND_COLORS = { 'black': 40, 'red': 41, 'green': 42, 'yellow': 43, 'blue': 44, 'magenta': 45, 'cyan': 46, 'white': 47 } OPTIONS = { 'bold': 1, 'underscore': 4, 'blink': 5, 'reverse': 7, 'conceal': 8 } def style(fg, bg, options): codes = [] if fg: codes.append(FOREGROUND_COLORS[fg]) if bg: codes.append(BACKGROUND_COLORS[bg]) if options: if not isinstance(options, (list, tuple)): options = [options] for option in options: codes.append(OPTIONS[option]) return '\033[{}m'.format(';'.join(map(str, codes))) STYLES = { 'info': style('green', None, None), 'comment': style('yellow', None, None), 'error': style('white', 'red', None) } def is_decorated(): return sys.stdout.isatty() def colorize(style, text): if not is_decorated(): return text return '{}{}\033[0m'.format(STYLES[style], text) class Installer: METADATA_URL = 'https://pypi.org/pypi/poetry/json' VERSION_REGEX = re.compile( 'v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?' '(' '[._-]?' '(?:(stable|beta|b|RC|alpha|a|patch|pl|p)((?:[.-]?\d+)*)?)?' '([.-]?dev)?' ')?' '(?:\+[^\s]+)?' ) def __init__(self, version=None, preview=False): self._version = version self._preview = preview def allows_prereleases(self): return self._preview def run(self): print(colorize('info', 'Retrieving metadata')) with urlopen(self.METADATA_URL) as r: metadata = json.loads(r.read()) print('') releases = sorted( metadata['releases'].keys(), key=lambda r: ( '.'.join(self.VERSION_REGEX.match(r).groups()[:3]), 0 if self.VERSION_REGEX.match(r).group(5) else 1 ) ) if self._version and self._version not in releases: print(colorize( 'error', 'Version {} does not exist'.format(self._version) )) return 1 version = self._version if not version: for release in reversed(releases): m = self.VERSION_REGEX.match(release) if m.group(5) and not self.allows_prereleases(): continue version = release break try: import poetry poetry_version = poetry.__version__ except ImportError: poetry_version = None if poetry_version == version: print('Latest version already installed.') return 0 print('Installing version: ' + colorize('info', version)) return self.install(version) def install(self, version): # Most of the work will be delegated to pip with tempfile.TemporaryDirectory(prefix='poetry-installer-') as dir: dir = Path(dir) dist = dir / 'dist' print(' - Getting dependencies') self.call( 'pip', 'install', 'poetry=={}'.format(version), '--target', dist ) print(' - 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 if file.is_dir(): shutil.copytree(str(file), str(dest)) shutil.rmtree(str(file)) else: shutil.copy(str(file), str(dest)) os.unlink(str(file)) wheel_data = dist / 'poetry-{}.dist-info'.format(version) / 'WHEEL' with wheel_data.open() as f: wheel_data = Parser().parsestr(f.read()) tag = wheel_data['Tag'] # Repack everything and install print(' - Installing {}'.format(colorize('info', 'poetry'))) shutil.make_archive( str(dir / 'poetry-{}-{}'.format(version, tag)), format='zip', root_dir=str(dist) ) os.rename( str(dir / 'poetry-{}-{}.zip'.format(version, tag)), str(dir / 'poetry-{}-{}.whl'.format(version, tag)), ) self.call( 'pip', 'install', '--upgrade', '--no-deps', str(dir / 'poetry-{}-{}.whl'.format(version, tag)) ) print('') print( '{} ({}) successfully installed!'.format( colorize('info', 'poetry'), colorize('comment', version) ) ) def call(self, *args): return subprocess.check_output(args, stderr=subprocess.STDOUT) def main(): parser = argparse.ArgumentParser( description='Installs the latest (or given) version of poetry' ) parser.add_argument( '-p', '--preview', dest='preview', action='store_true', default=False ) parser.add_argument( '--version', dest='version' ) args = parser.parse_args() installer = Installer(version=args.version, preview=args.preview) return installer.run() if __name__ == '__main__': main() Loading
CHANGELOG.md +2 −1 Changes for CHANGELOG.md: 2 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -5,7 +5,8 @@ ### Added - Added compatibility with Python 3.4 and 3.5. - Added the `version` command to automatically bump the packages' version. - Added the `version` command to automatically bump the package's version. - Added a standalone installer to install `poetry` isolated. ### Changed Loading
docs/docs/index.md +26 −3 Changes for docs/docs/index.md: 26 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -12,12 +12,35 @@ on Windows, Linux and OSX. ## Installation Installing Poetry is as simple as: Poetry provides a custom installer that will install `poetry` isolated from the rest of your system by vendorizing its dependencies. This is the recommended way of installing `poetry`. ```bash pip install --user poetry 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. 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 python get-poetry.py --version 0.7.0 ``` !!!note In the future, Poetry will come with its standalone installer. Using `pip` to install `poetry` is also possible. ```bash pip install --user poetry ``` Be aware, however, that it will also install poetry's dependencies which might cause conflicts.
get-poetry.py 0 → 100644 +259 −0 Changes for get-poetry.py: 259 added lines, 0 removed lines. Original line number Diff line number Diff line """ This script will install poetry and its dependencies in isolation from the rest of the system. It does, in order: - Downloads the latest stable version of poetry. - Checks if the _vendor directory is empty. - If the _vendor directory is not empty, empties it. - Installs all dependencies in the _vendor directory This ensure that poetry will look for its dependencies inside the _vendor directory without tampering with the base system. Note, however, that installing poetry via pip will still work, since if poetry does not find the dependencies in the _vendor directory, it will look for them in the base system. """ import argparse import json import os import re import shutil import subprocess import sys import tempfile from email.parser import Parser from pathlib import Path from urllib.request import urlopen FOREGROUND_COLORS = { 'black': 30, 'red': 31, 'green': 32, 'yellow': 33, 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37 } BACKGROUND_COLORS = { 'black': 40, 'red': 41, 'green': 42, 'yellow': 43, 'blue': 44, 'magenta': 45, 'cyan': 46, 'white': 47 } OPTIONS = { 'bold': 1, 'underscore': 4, 'blink': 5, 'reverse': 7, 'conceal': 8 } def style(fg, bg, options): codes = [] if fg: codes.append(FOREGROUND_COLORS[fg]) if bg: codes.append(BACKGROUND_COLORS[bg]) if options: if not isinstance(options, (list, tuple)): options = [options] for option in options: codes.append(OPTIONS[option]) return '\033[{}m'.format(';'.join(map(str, codes))) STYLES = { 'info': style('green', None, None), 'comment': style('yellow', None, None), 'error': style('white', 'red', None) } def is_decorated(): return sys.stdout.isatty() def colorize(style, text): if not is_decorated(): return text return '{}{}\033[0m'.format(STYLES[style], text) class Installer: METADATA_URL = 'https://pypi.org/pypi/poetry/json' VERSION_REGEX = re.compile( 'v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?' '(' '[._-]?' '(?:(stable|beta|b|RC|alpha|a|patch|pl|p)((?:[.-]?\d+)*)?)?' '([.-]?dev)?' ')?' '(?:\+[^\s]+)?' ) def __init__(self, version=None, preview=False): self._version = version self._preview = preview def allows_prereleases(self): return self._preview def run(self): print(colorize('info', 'Retrieving metadata')) with urlopen(self.METADATA_URL) as r: metadata = json.loads(r.read()) print('') releases = sorted( metadata['releases'].keys(), key=lambda r: ( '.'.join(self.VERSION_REGEX.match(r).groups()[:3]), 0 if self.VERSION_REGEX.match(r).group(5) else 1 ) ) if self._version and self._version not in releases: print(colorize( 'error', 'Version {} does not exist'.format(self._version) )) return 1 version = self._version if not version: for release in reversed(releases): m = self.VERSION_REGEX.match(release) if m.group(5) and not self.allows_prereleases(): continue version = release break try: import poetry poetry_version = poetry.__version__ except ImportError: poetry_version = None if poetry_version == version: print('Latest version already installed.') return 0 print('Installing version: ' + colorize('info', version)) return self.install(version) def install(self, version): # Most of the work will be delegated to pip with tempfile.TemporaryDirectory(prefix='poetry-installer-') as dir: dir = Path(dir) dist = dir / 'dist' print(' - Getting dependencies') self.call( 'pip', 'install', 'poetry=={}'.format(version), '--target', dist ) print(' - 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 if file.is_dir(): shutil.copytree(str(file), str(dest)) shutil.rmtree(str(file)) else: shutil.copy(str(file), str(dest)) os.unlink(str(file)) wheel_data = dist / 'poetry-{}.dist-info'.format(version) / 'WHEEL' with wheel_data.open() as f: wheel_data = Parser().parsestr(f.read()) tag = wheel_data['Tag'] # Repack everything and install print(' - Installing {}'.format(colorize('info', 'poetry'))) shutil.make_archive( str(dir / 'poetry-{}-{}'.format(version, tag)), format='zip', root_dir=str(dist) ) os.rename( str(dir / 'poetry-{}-{}.zip'.format(version, tag)), str(dir / 'poetry-{}-{}.whl'.format(version, tag)), ) self.call( 'pip', 'install', '--upgrade', '--no-deps', str(dir / 'poetry-{}-{}.whl'.format(version, tag)) ) print('') print( '{} ({}) successfully installed!'.format( colorize('info', 'poetry'), colorize('comment', version) ) ) def call(self, *args): return subprocess.check_output(args, stderr=subprocess.STDOUT) def main(): parser = argparse.ArgumentParser( description='Installs the latest (or given) version of poetry' ) parser.add_argument( '-p', '--preview', dest='preview', action='store_true', default=False ) parser.add_argument( '--version', dest='version' ) args = parser.parse_args() installer = Installer(version=args.version, preview=args.preview) return installer.run() if __name__ == '__main__': main()