Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
python-poetry
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
open
python-poetry
Commits
2cc78814
Unverified
Commit
2cc78814
authored
Mar 07, 2018
by
Sébastien Eustace
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix extra installation
parent
4891cd25
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
196 additions
and
23 deletions
+196
-23
README.md
+4
-4
poetry/installation/installer.py
+47
-17
poetry/poetry.py
+7
-0
poetry/puzzle/solver.py
+2
-2
tests/fixtures/sample_project/pyproject.toml
+6
-0
tests/installation/fixtures/extras-with-dependencies.test
+52
-0
tests/installation/test_installer.py
+76
-0
tests/test_poetry.py
+2
-0
No files found.
README.md
View file @
2cc78814
...
...
@@ -248,7 +248,7 @@ the `--no-dev` option.
poetry install
--no-dev
```
You can also specify the extra
feature
s you want installed
You can also specify the extras you want installed
by passing the
`--E|--extras`
option (See
[
Extras
](
#extras
)
for more info)
```
bash
...
...
@@ -259,7 +259,7 @@ poetry install -E mysql -E pgsql
#### Options
*
`--no-dev`
: Do not install dev dependencies.
*
`-
f|--feature
s`
: Features to install (multiple values allowed).
*
`-
E|--extra
s`
: Features to install (multiple values allowed).
### update
...
...
@@ -635,7 +635,7 @@ name = "awesome"
mandatory
=
"^1.0"
# A list of all of the optional dependencies, some of which are included in the
#
above `feature
s`. They can be opted into by apps.
#
below `extra
s`. They can be opted into by apps.
psycopg2
=
{
version
=
"^2.7"
,
optional
=
true
}
mysqlclient
=
{
version
=
"^1.3"
,
optional
=
true
}
...
...
@@ -644,7 +644,7 @@ mysql = ["mysqlclient"]
pgsql
=
["psycopg2"]
```
When installing packages, you can specify
feature
s by using the
`-E|--extras`
option:
When installing packages, you can specify
extra
s by using the
`-E|--extras`
option:
```
bash
poet install
--extras
"mysql pgsql"
...
...
poetry/installation/installer.py
View file @
2cc78814
...
...
@@ -183,7 +183,7 @@ class Installer:
# We need to filter operations so that packages
# not compatible with the current system,
# or optional and not requested, are dropped
self
.
_filter_operations
(
ops
)
self
.
_filter_operations
(
ops
,
local_repo
)
self
.
_io
.
new_line
()
...
...
@@ -336,12 +336,11 @@ class Installer:
)
->
List
[
Operation
]:
installed_repo
=
InstalledRepository
.
load
(
self
.
_io
.
venv
)
ops
=
[]
extras
=
[]
for
extra_name
,
packages
in
self
.
_locker
.
lock_data
.
get
(
'extras'
)
.
items
():
if
extra_name
in
self
.
_extras
:
for
package
in
packages
:
extras
.
append
(
package
.
lower
())
extra_packages
=
[
p
.
name
for
p
in
self
.
_get_extra_packages
(
locked_repository
)
]
for
locked
in
locked_repository
.
packages
:
is_installed
=
False
for
installed
in
installed_repo
.
packages
:
...
...
@@ -349,7 +348,7 @@ class Installer:
is_installed
=
True
if
locked
.
category
==
'dev'
and
not
self
.
is_dev_mode
():
ops
.
append
(
Uninstall
(
locked
))
elif
locked
.
is_optional
()
and
locked
.
name
not
in
extra
s
:
elif
locked
.
optional
and
locked
.
name
not
in
extra_package
s
:
# Installed but optional and not requested in extras
ops
.
append
(
Uninstall
(
locked
))
elif
locked
.
version
!=
installed
.
version
:
...
...
@@ -360,14 +359,16 @@ class Installer:
if
not
is_installed
:
# If it's optional and not in required extras
# we do not install
if
locked
.
is_optional
()
and
locked
.
name
not
in
extra
s
:
if
locked
.
optional
and
locked
.
name
not
in
extra_package
s
:
continue
ops
.
append
(
Install
(
locked
))
return
ops
def
_filter_operations
(
self
,
ops
:
List
[
Operation
]):
def
_filter_operations
(
self
,
ops
:
List
[
Operation
],
repo
:
Repository
)
->
None
:
extra_packages
=
[
p
.
name
for
p
in
self
.
_get_extra_packages
(
repo
)]
for
op
in
ops
:
if
isinstance
(
op
,
Update
):
package
=
op
.
target_package
...
...
@@ -394,20 +395,49 @@ class Installer:
extras
[
extra
]
=
[
dep
.
name
for
dep
in
deps
]
else
:
extras
=
{}
for
extra
,
deps
in
self
.
_locker
.
lock_data
.
get
(
'extras'
,
{}):
for
extra
,
deps
in
self
.
_locker
.
lock_data
.
get
(
'extras'
,
{})
.
items
()
:
extras
[
extra
]
=
[
dep
.
lower
()
for
dep
in
deps
]
# If a package is optional and not requested
# in any extra we skip it
if
package
.
optional
:
drop
=
True
for
extra
in
self
.
_extras
:
if
extra
in
extras
and
package
.
name
in
extras
[
extra
]:
drop
=
False
continue
if
drop
:
if
package
.
name
not
in
extra_packages
:
op
.
skip
(
'Not required'
)
def
_get_extra_packages
(
self
,
repo
):
"""
Returns all packages required by extras.
Maybe we just let the solver handle it?
"""
if
self
.
_update
:
extras
=
{
k
:
[
d
.
name
for
d
in
v
]
for
k
,
v
in
self
.
_package
.
extras
.
items
()
}
else
:
extras
=
self
.
_locker
.
lock_data
.
get
(
'extras'
,
{})
extra_packages
=
[]
for
extra_name
,
packages
in
extras
.
items
():
if
extra_name
not
in
self
.
_extras
:
continue
extra_packages
+=
[
Dependency
(
p
,
'*'
)
for
p
in
packages
]
def
_extra_packages
(
packages
):
pkgs
=
[]
for
package
in
packages
:
for
pkg
in
repo
.
packages
:
if
pkg
.
name
==
package
.
name
:
pkgs
.
append
(
package
)
pkgs
+=
_extra_packages
(
pkg
.
requires
)
break
return
pkgs
return
_extra_packages
(
extra_packages
)
def
_get_installer
(
self
)
->
BaseInstaller
:
return
PipInstaller
(
self
.
_io
.
venv
,
self
.
_io
)
poetry/poetry.py
View file @
2cc78814
from
pathlib
import
Path
from
.__version__
import
__version__
from
.packages
import
Dependency
from
.packages
import
Locker
from
.packages
import
Package
from
.repositories
import
Pool
...
...
@@ -102,6 +103,12 @@ class Poetry:
for
name
,
constraint
in
local_config
[
'dev-dependencies'
]
.
items
():
package
.
add_dependency
(
name
,
constraint
,
category
=
'dev'
)
if
'extras'
in
local_config
:
for
extra_name
,
requirements
in
local_config
[
'extras'
]
.
items
():
package
.
extras
[
extra_name
]
=
[
Dependency
(
req
,
'*'
)
for
req
in
requirements
]
locker
=
Locker
(
poetry_file
.
with_suffix
(
'.lock'
),
local_config
)
return
cls
(
poetry_file
,
local_config
,
package
,
locker
)
poetry/puzzle/solver.py
View file @
2cc78814
...
...
@@ -24,7 +24,7 @@ class Solver:
self
.
_locked
=
locked
self
.
_io
=
io
def
solve
(
self
,
requested
,
fixed
=
None
)
->
List
[
Operation
]:
def
solve
(
self
,
requested
,
fixed
=
None
,
extras
=
None
)
->
List
[
Operation
]:
resolver
=
Resolver
(
Provider
(
self
.
_package
,
self
.
_pool
),
UI
(
self
.
_io
))
base
=
None
...
...
@@ -40,7 +40,7 @@ class Solver:
packages
=
[
v
.
payload
for
v
in
graph
.
vertices
.
values
()]
# Setting
categories
# Setting
info
for
vertex
in
graph
.
vertices
.
values
():
tags
=
self
.
_get_tags_for_vertex
(
vertex
,
requested
)
if
'main'
in
tags
[
'category'
]:
...
...
tests/fixtures/sample_project/pyproject.toml
View file @
2cc78814
...
...
@@ -24,6 +24,12 @@ pendulum = { git = "https://github.com/sdispater/pendulum.git", branch = "2.0" }
requests
=
{
version
=
"^2.18"
,
optional
=
true
,
extras=
[
"security"
]
}
pathlib2
=
{
version
=
"^2.2"
,
python
=
"~2.7"
}
orator
=
{
version
=
"^0.9"
,
optional
=
true
}
[tool.poetry.extras]
db
=
[
"orator"
]
[tool.poetry.dev-dependencies]
pytest
=
"~3.4"
...
...
tests/installation/fixtures/extras-with-dependencies.test
0 → 100644
View file @
2cc78814
[[
package
]]
name
=
"A"
version
=
"1.0"
description
=
""
category
=
"main"
optional
=
false
python
-
versions
=
"*"
platform
=
"*"
[[
package
]]
name
=
"B"
version
=
"1.0"
description
=
""
category
=
"main"
optional
=
false
python
-
versions
=
"*"
platform
=
"*"
[[
package
]]
name
=
"C"
version
=
"1.0"
description
=
""
category
=
"main"
optional
=
true
python
-
versions
=
"*"
platform
=
"*"
[
package
.
dependencies
]
D
=
"^1.0"
[[
package
]]
name
=
"D"
version
=
"1.1"
description
=
""
category
=
"main"
optional
=
true
python
-
versions
=
"*"
platform
=
"*"
[
extras
]
foo
=
[
"C"
]
[
metadata
]
python
-
versions
=
"*"
platform
=
"*"
content
-
hash
=
"123456789"
[
metadata
.
hashes
]
A
=
[]
B
=
[]
C
=
[]
D
=
[]
tests/installation/test_installer.py
View file @
2cc78814
...
...
@@ -10,6 +10,7 @@ from poetry.io import NullIO
from
poetry.packages
import
Locker
as
BaseLocker
from
poetry.repositories
import
Pool
from
poetry.repositories
import
Repository
from
poetry.repositories.installed_repository
import
InstalledRepository
from
tests.helpers
import
get_dependency
from
tests.helpers
import
get_package
...
...
@@ -43,6 +44,9 @@ class Locker(BaseLocker):
def
is_locked
(
self
)
->
bool
:
return
self
.
_locked
def
is_fresh
(
self
)
->
bool
:
return
True
def
_get_content_hash
(
self
)
->
str
:
return
'123456789'
...
...
@@ -87,6 +91,16 @@ def pool(repo):
@pytest.fixture
()
def
installed
():
original
=
InstalledRepository
.
load
InstalledRepository
.
load
=
lambda
_
:
InstalledRepository
()
yield
InstalledRepository
.
load
=
original
@pytest.fixture
()
def
locker
():
return
Locker
()
...
...
@@ -373,3 +387,65 @@ def test_run_installs_extras_if_requested(installer, locker, repo, package):
installer
=
installer
.
installer
assert
len
(
installer
.
installs
)
==
4
# A, B, C, D
def
test_run_installs_extras_with_deps_if_requested
(
installer
,
locker
,
repo
,
package
):
package
.
extras
[
'foo'
]
=
[
get_dependency
(
'C'
)
]
package_a
=
get_package
(
'A'
,
'1.0'
)
package_b
=
get_package
(
'B'
,
'1.0'
)
package_c
=
get_package
(
'C'
,
'1.0'
)
package_d
=
get_package
(
'D'
,
'1.1'
)
repo
.
add_package
(
package_a
)
repo
.
add_package
(
package_b
)
repo
.
add_package
(
package_c
)
repo
.
add_package
(
package_d
)
package
.
add_dependency
(
'A'
,
'^1.0'
)
package
.
add_dependency
(
'B'
,
'^1.0'
)
package
.
add_dependency
(
'C'
,
{
'version'
:
'^1.0'
,
'optional'
:
True
})
package_c
.
add_dependency
(
'D'
,
'^1.0'
)
installer
.
extras
([
'foo'
])
installer
.
run
()
expected
=
fixture
(
'extras-with-dependencies'
)
# Extras are pinned in lock
assert
locker
.
written_data
==
expected
# But should not be installed
installer
=
installer
.
installer
assert
len
(
installer
.
installs
)
==
4
# A, B, C, D
def
test_run_installs_extras_with_deps_if_requested_locked
(
installer
,
locker
,
repo
,
package
,
installed
):
locker
.
locked
(
True
)
locker
.
mock_lock_data
(
fixture
(
'extras-with-dependencies'
))
package
.
extras
[
'foo'
]
=
[
get_dependency
(
'C'
)
]
package_a
=
get_package
(
'A'
,
'1.0'
)
package_b
=
get_package
(
'B'
,
'1.0'
)
package_c
=
get_package
(
'C'
,
'1.0'
)
package_d
=
get_package
(
'D'
,
'1.1'
)
repo
.
add_package
(
package_a
)
repo
.
add_package
(
package_b
)
repo
.
add_package
(
package_c
)
repo
.
add_package
(
package_d
)
package
.
add_dependency
(
'A'
,
'^1.0'
)
package
.
add_dependency
(
'B'
,
'^1.0'
)
package
.
add_dependency
(
'C'
,
{
'version'
:
'^1.0'
,
'optional'
:
True
})
package_c
.
add_dependency
(
'D'
,
'^1.0'
)
installer
.
extras
([
'foo'
])
installer
.
run
()
# But should not be installed
installer
=
installer
.
installer
assert
len
(
installer
.
installs
)
==
4
# A, B, C, D
tests/test_poetry.py
View file @
2cc78814
...
...
@@ -50,3 +50,5 @@ def test_poetry():
assert
pathlib2
.
pretty_constraint
==
'^2.2'
assert
pathlib2
.
python_versions
==
'~2.7'
assert
not
pathlib2
.
is_optional
()
assert
'db'
in
package
.
extras
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment