1
0
Fork 0
mirror of https://github.com/python-poetry/install.python-poetry.org.git synced 2024-11-27 08:11:01 -05:00

Compare commits

..

1 commit

Author SHA1 Message Date
Tatsunori Uchino
07185e6663 Add PowerShell snippets for postinstall 2022-07-17 18:54:20 +09:00
7 changed files with 154 additions and 317 deletions

3
.flake8 Normal file
View file

@ -0,0 +1,3 @@
[flake8]
max-line-length = 88
ignore = E501, E203, W503

View file

@ -1,6 +0,0 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly

View file

@ -15,7 +15,7 @@ on:
- '**' - '**'
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} group: installer-${{ github.head_ref || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }} cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs: jobs:
@ -25,32 +25,27 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [Ubuntu, macOS, Windows] os: [Ubuntu, macOS, Windows]
python-version: python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11-dev"]
- "3.9"
- "3.10"
- "3.11"
- "3.12"
- "3.13"
args: args:
- "" - ""
- "--preview" - "--preview"
- "--git https://github.com/python-poetry/poetry.git" - "--git https://github.com/python-poetry/poetry.git"
- "--version 1.1.14"
include: include:
- os: Ubuntu - os: Ubuntu
image: ubuntu-22.04 image: ubuntu-latest
- os: Windows - os: Windows
image: windows-2022 image: windows-2022
- os: macOS - os: macOS
image: macos-12 image: macos-11
fail-fast: false
defaults: defaults:
run: run:
shell: bash shell: bash
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5 uses: actions/setup-python@v3
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
@ -63,10 +58,10 @@ jobs:
run: echo "$APPDATA\Python\Scripts" >> $GITHUB_PATH run: echo "$APPDATA\Python\Scripts" >> $GITHUB_PATH
- name: Install Poetry - name: Install Poetry
run: python install-poetry.py -y ${{ matrix.args }} run: python install-poetry.py -y
- name: Upload Failure Log - name: Upload Failure Log
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v2
if: failure() if: failure()
with: with:
name: poetry-installer-error.log name: poetry-installer-error.log
@ -86,56 +81,3 @@ jobs:
run: | run: |
python install-poetry.py -y --uninstall python install-poetry.py -y --uninstall
{ type poetry 2>/dev/null >&2 && exit 1; } || exit 0 { type poetry 2>/dev/null >&2 && exit 1; } || exit 0
# debian/ubuntu needs special testing due to various issues around python packaging
# and configuration
integration-ubuntu:
name: Integration Test / Ubuntu / ${{ matrix.tag }}
runs-on: ubuntu-latest
container: docker.io/ubuntu:${{ matrix.tag }}
strategy:
matrix:
tag:
- focal
- jammy
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
- name: Install Packages
run: |
apt-get -y update
apt-get -y install python3 ca-certificates
apt-get -y install --reinstall python3-distutils
- name: Update PATH
run: echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Install Poetry
env:
DEB_PYTHON_INSTALL_LAYOUT: "deb"
run: python3 install-poetry.py -y
- name: Upload Failure Log
uses: actions/upload-artifact@v4
if: failure()
with:
name: poetry-installer-error.log
path: poetry-installer-error-*.log
- name: Verify Installation
run: |
set -e
poetry new foobar
cd foobar
poetry config virtualenvs.in-project true
poetry env use python3
[ "$(poetry run python --version)" == "$(python3 --version)" ] \
|| { echo >&2 "ERROR: Virtual environment Python version do not match system version." && exit 1; }
- name: Uninstall Poetry
run: |
python3 install-poetry.py -y --uninstall
{ type poetry 2>/dev/null >&2 && exit 1; } || exit 0

View file

@ -1,20 +1,28 @@
repos: repos:
- repo: https://github.com/psf/black-pre-commit-mirror - repo: https://github.com/psf/black
rev: 23.9.1 rev: 22.6.0
hooks: hooks:
- id: black - id: black
- repo: https://github.com/PyCQA/flake8
rev: 4.0.1
hooks:
- id: flake8
additional_dependencies:
- flake8-bugbear==22.4.25
- repo: https://github.com/PyCQA/isort
rev: 5.10.1
hooks:
- id: isort
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0 rev: v4.3.0
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
- id: end-of-file-fixer - id: end-of-file-fixer
- id: debug-statements - id: debug-statements
- id: pretty-format-json - id: pretty-format-json
args: [--autofix] args:
- --autofix
- id: check-json - id: check-json
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.290
hooks:
- id: ruff

View file

@ -1,7 +1,4 @@
# Python Poetry Installer # Python Poetry Installer
[![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/)
This repository contains Poetry's official installation script, installer source and This repository contains Poetry's official installation script, installer source and
related hosting configuration. related hosting configuration.
@ -14,21 +11,16 @@ Poetry provides a custom installer that will install `poetry` isolated
from the rest of your system. from the rest of your system.
### osx / linux / bashonwindows / Windows+MinGW install instructions ### osx / linux / bashonwindows / Windows+MinGW install instructions
```bash ```bash
curl -sSL https://install.python-poetry.org | python3 - curl -sSL https://install.python-poetry.org | python -
``` ```
### windows powershell install instructions ### windows powershell install instructions
```powershell ```powershell
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py - (Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python -
``` ```
> If you have installed Python through the Microsoft Store, replace `py` with `python` in the command above.
**Warning**: The previous `get-poetry.py` installer is now deprecated, if you are currently using it **Warning**: The previous `get-poetry.py` installer is now deprecated, if you are currently using it
you should migrate to the new, supported, `install.python-poetry.org` installer. you should migrate to the new, supported, `install-poetry.py` installer.
The installer installs the `poetry` tool to Poetry's `bin` directory. This location depends on your system: The installer installs the `poetry` tool to Poetry's `bin` directory. This location depends on your system:
@ -49,88 +41,40 @@ poetry --version
If you see something like `Poetry (version 1.2.0)` then you are ready to use Poetry. If you see something like `Poetry (version 1.2.0)` then you are ready to use Poetry.
If you decide Poetry isn't your thing, you can completely remove it from your system 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 or by setting by running the installer again with the `--uninstall` option or by setting
the `POETRY_UNINSTALL` environment variable before executing the installer: the `POETRY_UNINSTALL` environment variable before executing the installer.
```bash ```bash
curl -sSL https://install.python-poetry.org | python3 - --uninstall python install-poetry.py --uninstall
curl -sSL https://install.python-poetry.org | POETRY_UNINSTALL=1 python3 - POETRY_UNINSTALL=1 python install-poetry.py
``` ```
By default, Poetry is installed into the user's platform-specific home directory. By default, Poetry is installed into the user's platform-specific home directory.
If you wish to change this, you may define the `POETRY_HOME` environment variable: If you wish to change this, you may define the `POETRY_HOME` environment variable:
```bash ```bash
curl -sSL https://install.python-poetry.org | POETRY_HOME=/etc/poetry python3 - POETRY_HOME=/etc/poetry python install-poetry.py
``` ```
If you want to install prerelease versions, you can do so by passing `--preview` option or by using the `POETRY_PREVIEW` If you want to install prerelease versions, you can do so by passing `--preview` option to `install-poetry.py`
environment variable: or by using the `POETRY_PREVIEW` environment variable:
```bash ```bash
curl -sSL https://install.python-poetry.org | python3 - --preview python install-poetry.py --preview
curl -sSL https://install.python-poetry.org | POETRY_PREVIEW=1 python3 - POETRY_PREVIEW=1 python install-poetry.py
``` ```
Similarly, if you want to install a specific version, you can use `--version` option or the `POETRY_VERSION` Similarly, if you want to install a specific version, you can use `--version` option or the `POETRY_VERSION`
environment variable: environment variable:
```bash ```bash
curl -sSL https://install.python-poetry.org | python3 - --version 1.2.0 python install-poetry.py --version 1.2.0
curl -sSL https://install.python-poetry.org | POETRY_VERSION=1.2.0 python3 - POETRY_VERSION=1.2.0 python install-poetry.py
``` ```
You can also install Poetry for a `git` repository by using the `--git` option: You can also install Poetry for a `git` repository by using the `--git` option:
```bash ```bash
curl -sSL https://install.python-poetry.org | python3 - --git https://github.com/python-poetry/poetry.git@master python install-poetry.py --git https://github.com/python-poetry/poetry.git@master
```` ````
> **Note**: The installer does not support Python < 3.6. **Note**: Note that the installer does not support Python < 3.6.
## Known Issues
### Debian/Ubuntu
On Debian and Ubuntu systems, there are various issues that maybe caused due to how
various Python standard library components are packaged and configured. The following
details issues we are presently aware of, and potential workarounds.
> **Note:** This can also affect WSL users on Windows.
#### Installation Layout
If you encounter an error similar to the following, this might be due to
[pypa/virtualenv#2350](https://github.com/pypa/virtualenv/issues/2350).
```console
FileNotFoundError: [Errno 2] No such file or directory: '/root/.local/share/pypoetry/venv/bin/python'
```
You can work around this issue by setting the `DEB_PYTHON_INSTALL_LAYOUT` environment
variable to `deb` in order to emulate previously working behaviour.
```bash
export DEB_PYTHON_INSTALL_LAYOUT=deb
```
#### Missing `distutils` Module
In certain Debian/Ubuntu environments, you might encounter the following error message
in error logs (`poetry-installer-error-*.log`) provided when the installer fails.
```console
ModuleNotFoundError: No module named 'distutils.cmd'
```
This is probably due to [this bug](https://bugs.launchpad.net/ubuntu/+source/python3.10/+bug/1940705).
See also [pypa/get-pip#124](https://github.com/pypa/get-pip/issues/124).
The known workaround for this issue is to reinstall the `distutils` package provided by
the distribution.
```bash
apt-get install --reinstall python3-distutils
```
If you have installed a specific python version, eg: `3.10`, you might have to use the
package name `python3.10-distutils`.

View file

@ -1,43 +1,27 @@
#!/usr/bin/env python3 """
r""" This script will install Poetry and its dependencies.
This script will install Poetry and its dependencies in an isolated fashion.
It will perform the following steps: It does, in order:
* Create a new virtual environment using the built-in venv module, or the virtualenv zipapp if venv is unavailable.
This will be created at a platform-specific path (or `$POETRY_HOME` if `$POETRY_HOME` is set:
- `~/Library/Application Support/pypoetry` on macOS
- `$XDG_DATA_HOME/pypoetry` on Linux/Unix (`$XDG_DATA_HOME` is `~/.local/share` if unset)
- `%APPDATA%\pypoetry` on Windows
* Update pip inside the virtual environment to avoid bugs in older versions.
* Install the latest (or a given) version of Poetry inside this virtual environment using pip.
* Install a `poetry` script into a platform-specific path (or `$POETRY_HOME/bin` if `$POETRY_HOME` is set):
- `~/.local/bin` on Unix
- `%APPDATA%\Python\Scripts` on Windows
* Attempt to inform the user if they need to add this bin directory to their `$PATH`, as well as how to do so.
* Upon failure, write an error log to `poetry-installer-error-<hash>.log and restore any previous environment.
This script performs minimal magic, and should be relatively stable. However, it is optimized for interactive developer
use and trivial pipelines. If you are considering using this script in production, you should consider manually-managed
installs, or use of pipx as alternatives to executing arbitrary, unversioned code from the internet. If you prefer this
script to alternatives, consider maintaining a local copy as part of your infrastructure.
For full documentation, visit https://python-poetry.org/docs/#installation.
""" # noqa: E501
import sys
# Eager version check so we fail nicely before possible syntax errors
if sys.version_info < (3, 6): # noqa: UP036
sys.stdout.write("Poetry installer requires Python 3.6 or newer to run!\n")
sys.exit(1)
- Creates a virtual environment using venv (or virtualenv zipapp) in the correct OS data dir which will be
- `%APPDATA%\\pypoetry` on Windows
- ~/Library/Application Support/pypoetry on MacOS
- `${XDG_DATA_HOME}/pypoetry` (or `~/.local/share/pypoetry` if it's not set) on UNIX systems
- In `${POETRY_HOME}` if it's set.
- Installs the latest or given version of Poetry inside this virtual environment.
- Installs a `poetry` script in the Python user directory (or `${POETRY_HOME/bin}` if `POETRY_HOME` is set).
- On failure, the error log is written to poetry-installer-error-*.log and any previously existing environment
is restored.
"""
import argparse import argparse
import json import json
import os import os
import re import re
import shutil import shutil
import site
import subprocess import subprocess
import sys
import sysconfig import sysconfig
import tempfile import tempfile
@ -114,8 +98,8 @@ def is_decorated():
if WINDOWS: if WINDOWS:
return ( return (
os.getenv("ANSICON") is not None os.getenv("ANSICON") is not None
or os.getenv("ConEmuANSI") == "ON" # noqa: SIM112 or "ON" == os.getenv("ConEmuANSI")
or os.getenv("Term") == "xterm" # noqa: SIM112 or "xterm" == os.getenv("Term")
) )
if not hasattr(sys.stdout, "fileno"): if not hasattr(sys.stdout, "fileno"):
@ -141,7 +125,7 @@ def colorize(style, text):
if not is_decorated(): if not is_decorated():
return text return text
return f"{STYLES[style]}{text}\033[0m" return "{}{}\033[0m".format(STYLES[style], text)
def string_to_bool(value): def string_to_bool(value):
@ -150,29 +134,38 @@ def string_to_bool(value):
return value in {"true", "1", "y", "yes"} return value in {"true", "1", "y", "yes"}
def data_dir() -> Path: def data_dir(version: Optional[str] = None) -> Path:
if os.getenv("POETRY_HOME"): if os.getenv("POETRY_HOME"):
return Path(os.getenv("POETRY_HOME")).expanduser() return Path(os.getenv("POETRY_HOME")).expanduser()
if WINDOWS: if WINDOWS:
base_dir = Path(_get_win_folder("CSIDL_APPDATA")) const = "CSIDL_APPDATA"
path = os.path.normpath(_get_win_folder(const))
path = os.path.join(path, "pypoetry")
elif MACOS: elif MACOS:
base_dir = Path("~/Library/Application Support").expanduser() path = os.path.expanduser("~/Library/Application Support/pypoetry")
else: else:
base_dir = Path(os.getenv("XDG_DATA_HOME", "~/.local/share")).expanduser() path = os.getenv("XDG_DATA_HOME", os.path.expanduser("~/.local/share"))
path = os.path.join(path, "pypoetry")
base_dir = base_dir.resolve() if version:
return base_dir / "pypoetry" path = os.path.join(path, version)
return Path(path)
def bin_dir() -> Path: def bin_dir(version: Optional[str] = None) -> Path:
if os.getenv("POETRY_HOME"): if os.getenv("POETRY_HOME"):
return Path(os.getenv("POETRY_HOME")).expanduser() / "bin" return Path(os.getenv("POETRY_HOME"), "bin").expanduser()
user_base = site.getuserbase()
if WINDOWS and not MINGW: if WINDOWS and not MINGW:
return Path(_get_win_folder("CSIDL_APPDATA")) / "Python/Scripts" bin_dir = os.path.join(user_base, "Scripts")
else: else:
return Path("~/.local/bin").expanduser() bin_dir = os.path.join(user_base, "bin")
return Path(bin_dir)
def _get_win_folder_from_registry(csidl_name): def _get_win_folder_from_registry(csidl_name):
@ -188,9 +181,9 @@ def _get_win_folder_from_registry(csidl_name):
_winreg.HKEY_CURRENT_USER, _winreg.HKEY_CURRENT_USER,
r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders", r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders",
) )
path, _ = _winreg.QueryValueEx(key, shell_folder_name) dir, type = _winreg.QueryValueEx(key, shell_folder_name)
return path return dir
def _get_win_folder_with_ctypes(csidl_name): def _get_win_folder_with_ctypes(csidl_name):
@ -272,7 +265,7 @@ You can execute `set -U fish_user_paths {poetry_home_bin} $fish_user_paths`
POST_MESSAGE_CONFIGURE_WINDOWS = """ POST_MESSAGE_CONFIGURE_WINDOWS = """
You can choose and execute one of the following commands in PowerShell: You can choose and execute one of the following commands in PowerShell:
A. Append the bin directory to your user environment variable `PATH`: A. Appends the bin directory to your user environment variable `PATH`:
``` ```
[Environment]::SetEnvironmentVariable("Path", [Environment]::GetEnvironmentVariable("Path", "User") + ";{poetry_home_bin}", "User") [Environment]::SetEnvironmentVariable("Path", [Environment]::GetEnvironmentVariable("Path", "User") + ";{poetry_home_bin}", "User")
@ -288,7 +281,7 @@ echo 'if (-not (Get-Command poetry -ErrorAction Ignore)) {{ $env:Path += ";{poet
class PoetryInstallationError(RuntimeError): class PoetryInstallationError(RuntimeError):
def __init__(self, return_code: int = 0, log: Optional[str] = None): def __init__(self, return_code: int = 0, log: Optional[str] = None):
super().__init__() super(PoetryInstallationError, self).__init__()
self.return_code = return_code self.return_code = return_code
self.log = log self.log = log
@ -299,7 +292,7 @@ class VirtualEnvironment:
self._bin_path = self._path.joinpath( self._bin_path = self._path.joinpath(
"Scripts" if WINDOWS and not MINGW else "bin" "Scripts" if WINDOWS and not MINGW else "bin"
) )
# str is for compatibility with subprocess.run on CPython <= 3.7 on Windows # str is required for compatibility with subprocess run on CPython <= 3.7 on Windows
self._python = str( self._python = str(
self._path.joinpath(self._bin_path, "python.exe" if WINDOWS else "python") self._path.joinpath(self._bin_path, "python.exe" if WINDOWS else "python")
) )
@ -314,18 +307,7 @@ class VirtualEnvironment:
@classmethod @classmethod
def make(cls, target: Path) -> "VirtualEnvironment": def make(cls, target: Path) -> "VirtualEnvironment":
if not sys.executable:
raise ValueError(
"Unable to determine sys.executable. Set PATH to a sane value or set it"
" explicitly with PYTHONEXECUTABLE."
)
try: try:
# on some linux distributions (eg: debian), the distribution provided python
# installation might not include ensurepip, causing the venv module to
# fail when attempting to create a virtual environment
# we import ensurepip but do not use it explicitly here
import ensurepip # noqa: F401
import venv import venv
builder = venv.EnvBuilder(clear=True, with_pip=True, symlinks=False) builder = venv.EnvBuilder(clear=True, with_pip=True, symlinks=False)
@ -362,7 +344,7 @@ class VirtualEnvironment:
env = cls(target) env = cls(target)
# this ensures that outdated system default pip does not trigger older bugs # we do this here to ensure that outdated system default pip does not trigger older bugs
env.pip("install", "--disable-pip-version-check", "--upgrade", "pip") env.pip("install", "--disable-pip-version-check", "--upgrade", "pip")
return env return env
@ -394,32 +376,32 @@ class Cursor:
self._output = sys.stdout self._output = sys.stdout
def move_up(self, lines: int = 1) -> "Cursor": def move_up(self, lines: int = 1) -> "Cursor":
self._output.write(f"\x1b[{lines}A") self._output.write("\x1b[{}A".format(lines))
return self return self
def move_down(self, lines: int = 1) -> "Cursor": def move_down(self, lines: int = 1) -> "Cursor":
self._output.write(f"\x1b[{lines}B") self._output.write("\x1b[{}B".format(lines))
return self return self
def move_right(self, columns: int = 1) -> "Cursor": def move_right(self, columns: int = 1) -> "Cursor":
self._output.write(f"\x1b[{columns}C") self._output.write("\x1b[{}C".format(columns))
return self return self
def move_left(self, columns: int = 1) -> "Cursor": def move_left(self, columns: int = 1) -> "Cursor":
self._output.write(f"\x1b[{columns}D") self._output.write("\x1b[{}D".format(columns))
return self return self
def move_to_column(self, column: int) -> "Cursor": def move_to_column(self, column: int) -> "Cursor":
self._output.write(f"\x1b[{column}G") self._output.write("\x1b[{}G".format(column))
return self return self
def move_to_position(self, column: int, row: int) -> "Cursor": def move_to_position(self, column: int, row: int) -> "Cursor":
self._output.write(f"\x1b[{row + 1};{column}H") self._output.write("\x1b[{};{}H".format(row + 1, column))
return self return self
@ -504,26 +486,9 @@ class Installer:
self._accept_all = accept_all self._accept_all = accept_all
self._git = git self._git = git
self._path = path self._path = path
self._data_dir = data_dir()
self._bin_dir = bin_dir()
self._cursor = Cursor() self._cursor = Cursor()
self._bin_dir = None
self._data_dir = None
@property
def bin_dir(self) -> Path:
if not self._bin_dir:
self._bin_dir = bin_dir()
return self._bin_dir
@property
def data_dir(self) -> Path:
if not self._data_dir:
self._data_dir = data_dir()
return self._data_dir
@property
def version_file(self) -> Path:
return self.data_dir.joinpath("VERSION")
def allows_prereleases(self) -> bool: def allows_prereleases(self) -> bool:
return self._preview return self._preview
@ -549,20 +514,18 @@ class Installer:
mx = self.VERSION_REGEX.match(x) mx = self.VERSION_REGEX.match(x)
if mx is None: if mx is None:
# the version is not semver, perhaps scm or file # the version is not semver, perhaps scm or file, we assume upgrade is supported
# we assume upgrade is supported
return True return True
vx = (*tuple(int(p) for p in mx.groups()[:3]), mx.group(5)) vx = tuple(int(p) for p in mx.groups()[:3]) + (mx.group(5),)
return vx >= (1, 1, 7) return vx >= (1, 1, 7)
if version and not _is_self_upgrade_supported(version): if version and not _is_self_upgrade_supported(version):
self._write( self._write(
colorize( colorize(
"warning", "warning",
f"You are installing {version}. When using the current installer, " f"You are installing {version}. When using the current installer, this version does not support "
"this version does not support updating using the 'self update' " f"updating using the 'self update' command. Please use 1.1.7 or later.",
"command. Please use 1.1.7 or later.",
) )
) )
if not self._accept_all: if not self._accept_all:
@ -575,14 +538,14 @@ class Installer:
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
raise PoetryInstallationError( raise PoetryInstallationError(
return_code=e.returncode, log=e.output.decode() return_code=e.returncode, log=e.output.decode()
) from e )
self._write("") self._write("")
self.display_post_message(version) self.display_post_message(version)
return 0 return 0
def install(self, version): def install(self, version, upgrade=False):
""" """
Installs Poetry in $POETRY_HOME. Installs Poetry in $POETRY_HOME.
""" """
@ -595,13 +558,13 @@ class Installer:
with self.make_env(version) as env: with self.make_env(version) as env:
self.install_poetry(version, env) self.install_poetry(version, env)
self.make_bin(version, env) self.make_bin(version, env)
self.version_file.write_text(version) self._data_dir.joinpath("VERSION").write_text(version)
self._install_comment(version, "Done") self._install_comment(version, "Done")
return 0 return 0
def uninstall(self) -> int: def uninstall(self) -> int:
if not self.data_dir.exists(): if not self._data_dir.exists():
self._write( self._write(
"{} is not currently installed.".format(colorize("info", "Poetry")) "{} is not currently installed.".format(colorize("info", "Poetry"))
) )
@ -609,8 +572,8 @@ class Installer:
return 1 return 1
version = None version = None
if self.version_file.exists(): if self._data_dir.joinpath("VERSION").exists():
version = self.version_file.read_text().strip() version = self._data_dir.joinpath("VERSION").read_text().strip()
if version: if version:
self._write( self._write(
@ -621,10 +584,10 @@ class Installer:
else: else:
self._write("Removing {}".format(colorize("info", "Poetry"))) self._write("Removing {}".format(colorize("info", "Poetry")))
shutil.rmtree(str(self.data_dir)) shutil.rmtree(str(self._data_dir))
for script in ["poetry", "poetry.bat", "poetry.exe"]: for script in ["poetry", "poetry.bat"]:
if self.bin_dir.joinpath(script).exists(): if self._bin_dir.joinpath(script).exists():
self.bin_dir.joinpath(script).unlink() self._bin_dir.joinpath(script).unlink()
return 0 return 0
@ -639,7 +602,7 @@ class Installer:
@contextmanager @contextmanager
def make_env(self, version: str) -> VirtualEnvironment: def make_env(self, version: str) -> VirtualEnvironment:
env_path = self.data_dir.joinpath("venv") env_path = self._data_dir.joinpath("venv")
env_path_saved = env_path.with_suffix(".save") env_path_saved = env_path.with_suffix(".save")
if env_path.exists(): if env_path.exists():
@ -671,20 +634,20 @@ class Installer:
def make_bin(self, version: str, env: VirtualEnvironment) -> None: def make_bin(self, version: str, env: VirtualEnvironment) -> None:
self._install_comment(version, "Creating script") self._install_comment(version, "Creating script")
self.bin_dir.mkdir(parents=True, exist_ok=True) self._bin_dir.mkdir(parents=True, exist_ok=True)
script = "poetry.exe" if WINDOWS else "poetry" script = "poetry.exe" if WINDOWS else "poetry"
target_script = env.bin_path.joinpath(script) target_script = env.bin_path.joinpath(script)
if self.bin_dir.joinpath(script).exists(): if self._bin_dir.joinpath(script).exists():
self.bin_dir.joinpath(script).unlink() self._bin_dir.joinpath(script).unlink()
try: try:
self.bin_dir.joinpath(script).symlink_to(target_script) self._bin_dir.joinpath(script).symlink_to(target_script)
except OSError: except OSError:
# This can happen if the user # This can happen if the user
# does not have the correct permission on Windows # does not have the correct permission on Windows
shutil.copy(target_script, self.bin_dir.joinpath(script)) shutil.copy(target_script, self._bin_dir.joinpath(script))
def install_poetry(self, version: str, env: VirtualEnvironment) -> None: def install_poetry(self, version: str, env: VirtualEnvironment) -> None:
self._install_comment(version, "Installing Poetry") self._install_comment(version, "Installing Poetry")
@ -701,7 +664,7 @@ class Installer:
def display_pre_message(self) -> None: def display_pre_message(self) -> None:
kwargs = { kwargs = {
"poetry": colorize("info", "Poetry"), "poetry": colorize("info", "Poetry"),
"poetry_home_bin": colorize("comment", self.bin_dir), "poetry_home_bin": colorize("comment", self._bin_dir),
} }
self._write(PRE_MESSAGE.format(**kwargs)) self._write(PRE_MESSAGE.format(**kwargs))
@ -718,17 +681,17 @@ class Installer:
path = self.get_windows_path_var() path = self.get_windows_path_var()
message = POST_MESSAGE_NOT_IN_PATH message = POST_MESSAGE_NOT_IN_PATH
if path and str(self.bin_dir) in path: if path and str(self._bin_dir) in path:
message = POST_MESSAGE message = POST_MESSAGE
self._write( self._write(
message.format( message.format(
poetry=colorize("info", "Poetry"), poetry=colorize("info", "Poetry"),
version=colorize("b", version), version=colorize("b", version),
poetry_home_bin=colorize("comment", self.bin_dir), poetry_home_bin=colorize("comment", self._bin_dir),
poetry_executable=colorize("b", self.bin_dir.joinpath("poetry")), poetry_executable=colorize("b", self._bin_dir.joinpath("poetry")),
configure_message=POST_MESSAGE_CONFIGURE_WINDOWS.format( configure_message=POST_MESSAGE_CONFIGURE_WINDOWS.format(
poetry_home_bin=colorize("comment", self.bin_dir) poetry_home_bin=colorize("comment", self._bin_dir)
), ),
test_command=colorize("b", "poetry --version"), test_command=colorize("b", "poetry --version"),
) )
@ -737,12 +700,11 @@ class Installer:
def get_windows_path_var(self) -> Optional[str]: def get_windows_path_var(self) -> Optional[str]:
import winreg import winreg
with winreg.ConnectRegistry( with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as root:
None, winreg.HKEY_CURRENT_USER with winreg.OpenKey(root, "Environment", 0, winreg.KEY_ALL_ACCESS) as key:
) as root, winreg.OpenKey(root, "Environment", 0, winreg.KEY_ALL_ACCESS) as key: path, _ = winreg.QueryValueEx(key, "PATH")
path, _ = winreg.QueryValueEx(key, "PATH")
return path return path
def display_post_message_fish(self, version: str) -> None: def display_post_message_fish(self, version: str) -> None:
fish_user_paths = subprocess.check_output( fish_user_paths = subprocess.check_output(
@ -750,17 +712,17 @@ class Installer:
).decode("utf-8") ).decode("utf-8")
message = POST_MESSAGE_NOT_IN_PATH message = POST_MESSAGE_NOT_IN_PATH
if fish_user_paths and str(self.bin_dir) in fish_user_paths: if fish_user_paths and str(self._bin_dir) in fish_user_paths:
message = POST_MESSAGE message = POST_MESSAGE
self._write( self._write(
message.format( message.format(
poetry=colorize("info", "Poetry"), poetry=colorize("info", "Poetry"),
version=colorize("b", version), version=colorize("b", version),
poetry_home_bin=colorize("comment", self.bin_dir), poetry_home_bin=colorize("comment", self._bin_dir),
poetry_executable=colorize("b", self.bin_dir.joinpath("poetry")), poetry_executable=colorize("b", self._bin_dir.joinpath("poetry")),
configure_message=POST_MESSAGE_CONFIGURE_FISH.format( configure_message=POST_MESSAGE_CONFIGURE_FISH.format(
poetry_home_bin=colorize("comment", self.bin_dir) poetry_home_bin=colorize("comment", self._bin_dir)
), ),
test_command=colorize("b", "poetry --version"), test_command=colorize("b", "poetry --version"),
) )
@ -770,30 +732,30 @@ class Installer:
paths = os.getenv("PATH", "").split(":") paths = os.getenv("PATH", "").split(":")
message = POST_MESSAGE_NOT_IN_PATH message = POST_MESSAGE_NOT_IN_PATH
if paths and str(self.bin_dir) in paths: if paths and str(self._bin_dir) in paths:
message = POST_MESSAGE message = POST_MESSAGE
self._write( self._write(
message.format( message.format(
poetry=colorize("info", "Poetry"), poetry=colorize("info", "Poetry"),
version=colorize("b", version), version=colorize("b", version),
poetry_home_bin=colorize("comment", self.bin_dir), poetry_home_bin=colorize("comment", self._bin_dir),
poetry_executable=colorize("b", self.bin_dir.joinpath("poetry")), poetry_executable=colorize("b", self._bin_dir.joinpath("poetry")),
configure_message=POST_MESSAGE_CONFIGURE_UNIX.format( configure_message=POST_MESSAGE_CONFIGURE_UNIX.format(
poetry_home_bin=colorize("comment", self.bin_dir) poetry_home_bin=colorize("comment", self._bin_dir)
), ),
test_command=colorize("b", "poetry --version"), test_command=colorize("b", "poetry --version"),
) )
) )
def ensure_directories(self) -> None: def ensure_directories(self) -> None:
self.data_dir.mkdir(parents=True, exist_ok=True) self._data_dir.mkdir(parents=True, exist_ok=True)
self.bin_dir.mkdir(parents=True, exist_ok=True) self._bin_dir.mkdir(parents=True, exist_ok=True)
def get_version(self): def get_version(self):
current_version = None current_version = None
if self.version_file.exists(): if self._data_dir.joinpath("VERSION").exists():
current_version = self.version_file.read_text().strip() current_version = self._data_dir.joinpath("VERSION").read_text().strip()
self._write(colorize("info", "Retrieving Poetry metadata")) self._write(colorize("info", "Retrieving Poetry metadata"))
@ -803,8 +765,8 @@ class Installer:
mx = self.VERSION_REGEX.match(x) mx = self.VERSION_REGEX.match(x)
my = self.VERSION_REGEX.match(y) my = self.VERSION_REGEX.match(y)
vx = (*tuple(int(p) for p in mx.groups()[:3]), mx.group(5)) vx = tuple(int(p) for p in mx.groups()[:3]) + (mx.group(5),)
vy = (*tuple(int(p) for p in my.groups()[:3]), my.group(5)) vy = tuple(int(p) for p in my.groups()[:3]) + (my.group(5),)
if vx < vy: if vx < vy:
return -1 return -1
@ -819,7 +781,7 @@ class Installer:
) )
if self._version and self._version not in releases: if self._version and self._version not in releases:
msg = f"Version {self._version} does not exist." msg = "Version {} does not exist.".format(self._version)
self._write(colorize("error", msg)) self._write(colorize("error", msg))
raise ValueError(msg) raise ValueError(msg)
@ -837,7 +799,9 @@ class Installer:
if current_version == version and not self._force: if current_version == version and not self._force:
self._write( self._write(
f'The latest version ({colorize("b", version)}) is already installed.' "The latest version ({}) is already installed.".format(
colorize("b", version)
)
) )
return None, current_version return None, current_version
@ -948,8 +912,7 @@ def main():
text=True, text=True,
) )
installer._write(colorize("error", f"See {path} for error logs.")) installer._write(colorize("error", f"See {path} for error logs."))
tb = "".join(traceback.format_tb(e.__traceback__)) text = f"{e.log}\nTraceback:\n\n{''.join(traceback.format_tb(e.__traceback__))}"
text = f"{e.log}\nTraceback:\n\n{tb}"
Path(path).write_text(text) Path(path).write_text(text)
return e.return_code return e.return_code

View file

@ -1,29 +1,12 @@
[tool.ruff] [tool.isort]
fix = true profile = "black"
unfixable = [ force_single_line = true
"ERA", # do not autoremove commented out code atomic = true
] lines_after_imports = 2
target-version = "py37" lines_between_types = 1
filter_files = true
[tool.black]
line-length = 88 line-length = 88
extend-select = [ include = '\.pyi?$'
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"ERA", # flake8-eradicate/eradicate
"I", # isort
"N", # pep8-naming
"PIE", # flake8-pie
"PGH", # pygrep
"RUF", # ruff checks
"SIM", # flake8-simplify
"TCH", # flake8-type-checking
"TID", # flake8-tidy-imports
"UP", # pyupgrade
]
[tool.ruff.flake8-tidy-imports]
ban-relative-imports = "all"
[tool.ruff.isort]
force-single-line = true
lines-between-types = 1
lines-after-imports = 2