Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -491,3 +491,19 @@ The `cache list` command lists Poetry's available caches.
```bash
poetry cache list
```

### cache clear

The `cache clear` command removes packages from a cached repository.

For example, to clear the whole cache of packages from the `pypi` repository, run:

```bash
poetry cache clear pypi --all
```

To only remove a specific package from a cache, you have to specify the cache entry in the following form `cache:package:version`:

```bash
poetry cache clear pypi:requests:2.24.0
```
4 changes: 3 additions & 1 deletion docs/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ Defaults to one of the following directories:
Create a new virtual environment if one doesn't already exist.
Defaults to `true`.

If set to `false`, poetry will install dependencies into the current python environment.

!!!note:
When setting this configuration to `false`, the Python environment used must have `pip`
installed and available.
Expand All @@ -116,7 +118,7 @@ Defaults to `true`.
Create the virtualenv inside the project's root directory.
Defaults to `None`.

If set to `true`, the virtualenv wil be created and expected in a folder named `.venv` within the root directory of the project.
If set to `true`, the virtualenv will be created and expected in a folder named `.venv` within the root directory of the project.

If not set explicitly (default), `poetry` will use the virtualenv from the `.venv` directory when one is available. If set to `false`, `poetry` will ignore any existing `.venv` directory.

Expand Down
6 changes: 3 additions & 3 deletions get-poetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,15 +159,15 @@ def colorize(style, text):
def temporary_directory(*args, **kwargs):
try:
from tempfile import TemporaryDirectory

with TemporaryDirectory(*args, **kwargs) as name:
yield name
except ImportError:
name = tempfile.mkdtemp(*args, **kwargs)

yield name

shutil.rmtree(name)
else:
with TemporaryDirectory(*args, **kwargs) as name:
yield name


def string_to_bool(value):
Expand Down
2 changes: 1 addition & 1 deletion poetry/console/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def __init__(self):
"See <fg=blue>https://python-poetry.org/docs/managing-environments/</> "
"for more information."
).format(python_version, poetry_feature_release, python_version)
self._preliminary_io.write_line("<fg=yellow>{}</>\n".format(message))
self._preliminary_io.error_line("<fg=yellow>{}</>\n".format(message))

@property
def poetry(self):
Expand Down
2 changes: 1 addition & 1 deletion poetry/console/commands/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ def handle(self):
)

builder = Builder(self.poetry)
builder.build(fmt)
builder.build(fmt, executable=self.env.python)
3 changes: 2 additions & 1 deletion poetry/console/commands/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from cleo import option
from tomlkit import inline_table

from poetry.core.pyproject import PyProjectException
from poetry.core.pyproject.toml import PyProjectTOML
from poetry.utils._compat import OrderedDict
from poetry.utils._compat import Path
Expand Down Expand Up @@ -378,7 +379,7 @@ def _parse_requirements(

try:
cwd = self.poetry.file.parent
except RuntimeError:
except (PyProjectException, RuntimeError):
cwd = Path.cwd()

for requirement in requirements:
Expand Down
10 changes: 9 additions & 1 deletion poetry/console/commands/lock.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from cleo import option

from .installer_command import InstallerCommand


Expand All @@ -6,6 +8,12 @@ class LockCommand(InstallerCommand):
name = "lock"
description = "Locks the project dependencies."

options = [
option(
"no-update", None, "Do not update locked versions, only refresh lock file."
),
]

help = """
The <info>lock</info> command reads the <comment>pyproject.toml</> file from the
current directory, processes it, and locks the dependencies in the <comment>poetry.lock</>
Expand All @@ -21,6 +29,6 @@ def handle(self):
self.poetry.config.get("experimental.new-installer", False)
)

self._installer.lock()
self._installer.lock(update=not self.option("no-update"))

return self._installer.run()
4 changes: 3 additions & 1 deletion poetry/factory.py
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,13 @@ def create_poetry(

# Load local sources
repositories = {}
existing_repositories = config.get("repositories", {})
for source in base_poetry.pyproject.poetry_config.get("source", []):
name = source.get("name")
url = source.get("url")
if name and url:
repositories[name] = {"url": url}
if name not in existing_repositories:
repositories[name] = {"url": url}

config.merge({"repositories": repositories})

Expand Down
9 changes: 9 additions & 0 deletions poetry/inspection/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,15 @@ def to_package(
package.python_versions = self.requires_python or "*"
package.files = self.files

if root_dir or (self._source_type in {"directory"} and self._source_url):
# this is a local poetry project, this means we can extract "richer" requirement information
# eg: development requirements etc.
poetry_package = self._get_poetry_package(path=root_dir or self._source_url)
if poetry_package:
package.extras = poetry_package.extras
package.requires = poetry_package.requires
return package

for req in self.requires_dist or []:
try:
# Attempt to parse the PEP-508 requirement string
Expand Down
2 changes: 1 addition & 1 deletion poetry/installation/chooser.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def choose_for(self, package): # type: (Package) -> Link
):
continue

if link.ext == ".egg":
if link.ext in {".egg", ".exe", ".msi", ".rpm", ".srpm"}:
continue

links.append(link)
Expand Down
19 changes: 19 additions & 0 deletions poetry/installation/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,33 @@ def execute(self, operations): # type: (Operation) -> int
self._sections = OrderedDict()
for _, group in groups:
tasks = []
serial_operations = []
for operation in group:
if self._shutdown:
break

# Some operations are unsafe, we mus execute them serially in a group
# https://github.com/python-poetry/poetry/issues/3086
# https://github.com/python-poetry/poetry/issues/2658
#
# We need to explicitly check source type here, see:
# https://github.com/python-poetry/poetry-core/pull/98
is_parallel_unsafe = operation.job_type == "uninstall" or (
operation.package.develop
and operation.package.source_type in {"directory", "git"}
)
if not operation.skipped and is_parallel_unsafe:
serial_operations.append(operation)
continue

tasks.append(self._executor.submit(self._execute_operation, operation))

try:
wait(tasks)

for operation in serial_operations:
wait([self._executor.submit(self._execute_operation, operation)])

except KeyboardInterrupt:
self._shutdown = True

Expand Down
38 changes: 34 additions & 4 deletions poetry/installation/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ def set_locker(self, locker): # type: (Locker) -> Installer
return self

def run(self):
# Check if refresh
if not self._update and self._lock and self._locker.is_locked():
return self._do_refresh()

# Force update if there is no lock file present
if not self._update and not self._locker.is_locked():
self._update = True
Expand Down Expand Up @@ -137,11 +141,11 @@ def update(self, update=True): # type: (bool) -> Installer

return self

def lock(self): # type: () -> Installer
def lock(self, update=True): # type: (bool) -> Installer
"""
Prepare the installer for locking only.
"""
self.update()
self.update(update=update)
self.execute_operations(False)
self._lock = True

Expand Down Expand Up @@ -173,6 +177,32 @@ def use_executor(self, use_executor=True): # type: (bool) -> Installer

return self

def _do_refresh(self):
from poetry.puzzle import Solver

# Checking extras
for extra in self._extras:
if extra not in self._package.extras:
raise ValueError("Extra [{}] is not specified.".format(extra))

locked_repository = self._locker.locked_repository(True)
solver = Solver(
self._package,
self._pool,
locked_repository,
locked_repository,
self._io, # noqa
)

ops = solver.solve(use_latest=[])

local_repo = Repository()
self._populate_local_repo(local_repo, ops)

self._write_lock_file(local_repo, force=True)

return 0

def _do_install(self, local_repo):
from poetry.puzzle import Solver

Expand Down Expand Up @@ -285,8 +315,8 @@ def _do_install(self, local_repo):
# Execute operations
return self._execute(ops)

def _write_lock_file(self, repo): # type: (Repository) -> None
if self._update and self._write_lock:
def _write_lock_file(self, repo, force=True): # type: (Repository, bool) -> None
if force or (self._update and self._write_lock):
updated_lock = self._locker.set_lock_data(self._package, repo.packages)

if updated_lock:
Expand Down
Empty file modified poetry/mixology/partial_solution.py
100755 → 100644
Empty file.
Empty file modified poetry/mixology/term.py
100755 → 100644
Empty file.
Empty file modified poetry/mixology/version_solver.py
100755 → 100644
Empty file.
10 changes: 7 additions & 3 deletions poetry/packages/locker.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,9 @@ def locked_repository(
return packages

def get_project_dependencies(
self, project_requires, pinned_versions=False, with_nested=False
): # type: (List[Dependency], bool, bool) -> Any
packages = self.locked_repository().packages
self, project_requires, pinned_versions=False, with_nested=False, with_dev=False
): # type: (List[Dependency], bool, bool, bool) -> Any
packages = self.locked_repository(with_dev).packages

# group packages entries by name, this is required because requirement might use
# different constraints
Expand Down Expand Up @@ -230,6 +230,10 @@ def __get_locked_package(
# project level dependencies take precedence
continue

# we make a copy to avoid any side-effects
requirement = deepcopy(requirement)
requirement._category = pkg.category

if pinned_versions:
requirement.set_constraint(
__get_locked_package(requirement).to_dependency().constraint
Expand Down
30 changes: 13 additions & 17 deletions poetry/puzzle/provider.py
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ def complete_package(
self._pool.package(
package.name,
package.version.text,
extras=package.dependency.extras,
extras=list(package.dependency.extras),
repository=package.dependency.source_name,
),
)
Expand All @@ -453,23 +453,19 @@ def complete_package(
self.search_for_url(r)

optional_dependencies = []
activated_extras = []
for extra in package.dependency.extras:
if extra not in package.extras:
continue

activated_extras.append(extra)
optional_dependencies += [d.name for d in package.extras[extra]]

_dependencies = []

# If some extras/features were required, we need to
# add a special dependency representing the base package
# to the current package
if package.dependency.extras:
if activated_extras:
package = package.with_features(activated_extras)
for extra in package.dependency.extras:
if extra not in package.extras:
continue

optional_dependencies += [d.name for d in package.extras[extra]]

package = package.with_features(list(package.dependency.extras))
_dependencies.append(package.without_features().to_dependency())

for dep in requires:
Expand All @@ -482,12 +478,12 @@ def complete_package(
if self._env and not dep.marker.validate(self._env.marker_env):
continue

if (
dep.is_optional()
and dep.name not in optional_dependencies
and not package.is_root()
):
continue
if not package.is_root():
if (dep.is_optional() and dep.name not in optional_dependencies) or (
dep.in_extras
and not set(dep.in_extras).intersection(package.dependency.extras)
):
continue

_dependencies.append(dep)

Expand Down
Empty file modified poetry/repositories/legacy_repository.py
100755 → 100644
Empty file.
Empty file modified poetry/repositories/pool.py
100755 → 100644
Empty file.
Empty file modified poetry/repositories/pypi_repository.py
100755 → 100644
Empty file.
7 changes: 1 addition & 6 deletions poetry/repositories/repository.py
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,9 @@ def name(self):
def package(self, name, version, extras=None):
name = name.lower()

if extras is None:
extras = []

for package in self.packages:
if name == package.name and package.version.text == version:
package = package.with_features(extras)

return package
return package.clone()

def find_packages(self, dependency):
constraint = dependency.constraint
Expand Down
2 changes: 1 addition & 1 deletion poetry/utils/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,7 @@ def unset_env(self, key):
del os.environ[key]

def _updated_path(self):
return os.pathsep.join([str(self._bin_dir), os.environ["PATH"]])
return os.pathsep.join([str(self._bin_dir), os.environ.get("PATH", "")])


class NullEnv(SystemEnv):
Expand Down
Loading