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
2ed53be4
Unverified
Commit
2ed53be4
authored
Apr 26, 2019
by
Sébastien Eustace
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix root package installation with pip>=19.0
parent
457e2205
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
309 additions
and
22 deletions
+309
-22
.gitignore
+1
-0
CHANGELOG.md
+1
-0
poetry/console/commands/install.py
+4
-17
poetry/masonry/builders/__init__.py
+1
-0
poetry/masonry/builders/builder.py
+1
-1
poetry/masonry/builders/editable.py
+127
-0
poetry/utils/env.py
+44
-0
tests/masonry/builders/test_editable.py
+130
-0
tests/masonry/builders/test_wheel.py
+0
-4
No files found.
.gitignore
View file @
2ed53be4
...
...
@@ -35,3 +35,4 @@ MANIFEST.in
.venv
/releases/*
pip-wheel-metadata
CHANGELOG.md
View file @
2ed53be4
...
...
@@ -4,6 +4,7 @@
### Fixed
-
Fixed root package installation with
`pip>=19.0`
.
-
Fixed packages not being removed after using the
`remove`
command.
...
...
poetry/console/commands/install.py
View file @
2ed53be4
...
...
@@ -28,10 +28,8 @@ exist it will look for <comment>pyproject.toml</> and do the same.
def
handle
(
self
):
from
poetry.installation
import
Installer
from
poetry.io
import
NullIO
from
poetry.masonry.builders
import
Sdist
Builder
from
poetry.masonry.builders
import
Editable
Builder
from
poetry.masonry.utils.module
import
ModuleOrPackageNotFound
from
poetry.utils._compat
import
decode
from
poetry.utils.env
import
NullEnv
installer
=
Installer
(
self
.
output
,
...
...
@@ -60,7 +58,7 @@ exist it will look for <comment>pyproject.toml</> and do the same.
return
return_code
try
:
builder
=
SdistBuilder
(
self
.
poetry
,
NullEnv
()
,
NullIO
())
builder
=
EditableBuilder
(
self
.
poetry
,
self
.
_env
,
NullIO
())
except
ModuleOrPackageNotFound
:
# This is likely due to the fact that the project is an application
# not following the structure expected by Poetry
...
...
@@ -76,17 +74,6 @@ exist it will look for <comment>pyproject.toml</> and do the same.
if
self
.
option
(
"dry-run"
):
return
0
setup
=
self
.
poetry
.
file
.
parent
/
"setup.py"
has_setup
=
setup
.
exists
()
builder
.
build
()
if
has_setup
:
self
.
line
(
"<warning>A setup.py file already exists. Using it.</warning>"
)
else
:
with
setup
.
open
(
"w"
,
encoding
=
"utf-8"
)
as
f
:
f
.
write
(
decode
(
builder
.
build_setup
()))
try
:
self
.
env
.
run
(
"pip"
,
"install"
,
"-e"
,
str
(
setup
.
parent
),
"--no-deps"
)
finally
:
if
not
has_setup
:
os
.
remove
(
str
(
setup
))
return
0
poetry/masonry/builders/__init__.py
View file @
2ed53be4
from
.complete
import
CompleteBuilder
from
.editable
import
EditableBuilder
from
.sdist
import
SdistBuilder
from
.wheel
import
WheelBuilder
poetry/masonry/builders/builder.py
View file @
2ed53be4
...
...
@@ -35,7 +35,7 @@ class Builder(object):
AVAILABLE_PYTHONS
=
{
"2"
,
"2.7"
,
"3"
,
"3.4"
,
"3.5"
,
"3.6"
,
"3.7"
}
def
__init__
(
self
,
poetry
,
env
,
io
):
def
__init__
(
self
,
poetry
,
env
,
io
):
# type: (Poetry, Env, IO) -> None
self
.
_poetry
=
poetry
self
.
_env
=
env
self
.
_io
=
io
...
...
poetry/masonry/builders/editable.py
View file @
2ed53be4
from
__future__
import
unicode_literals
import
os
import
shutil
from
collections
import
defaultdict
from
poetry.semver.version
import
Version
from
poetry.utils._compat
import
decode
from
.builder
import
Builder
from
.sdist
import
SdistBuilder
class
EditableBuilder
(
Builder
):
def
build
(
self
):
if
self
.
_package
.
build
:
# If the project has some kind of special
# build needs we delegate to the setup.py file
return
self
.
_setup_build
()
self
.
_build_egg_info
()
self
.
_build_egg_link
()
self
.
_add_easy_install_entry
()
def
_setup_build
(
self
):
builder
=
SdistBuilder
(
self
.
_poetry
,
self
.
_env
,
self
.
_io
)
setup
=
self
.
_path
/
"setup.py"
has_setup
=
setup
.
exists
()
if
has_setup
:
self
.
_io
.
write_line
(
"<warning>A setup.py file already exists. Using it.</warning>"
)
else
:
with
setup
.
open
(
"w"
,
encoding
=
"utf-8"
)
as
f
:
f
.
write
(
decode
(
builder
.
build_setup
()))
try
:
if
self
.
_env
.
pip_version
<
Version
(
19
,
0
):
self
.
_env
.
run
(
"python"
,
"-m"
,
"pip"
,
"install"
,
"-e"
,
str
(
self
.
_path
))
else
:
# Temporarily rename pyproject.toml
shutil
.
move
(
str
(
self
.
_poetry
.
file
),
str
(
self
.
_poetry
.
file
.
with_suffix
(
".tmp"
))
)
try
:
self
.
_env
.
run
(
"python"
,
"-m"
,
"pip"
,
"install"
,
"-e"
,
str
(
self
.
_path
)
)
finally
:
shutil
.
move
(
str
(
self
.
_poetry
.
file
.
with_suffix
(
".tmp"
)),
str
(
self
.
_poetry
.
file
),
)
finally
:
if
not
has_setup
:
os
.
remove
(
str
(
setup
))
def
_build_egg_info
(
self
):
egg_info
=
self
.
_path
/
"{}.egg-info"
.
format
(
self
.
_package
.
name
.
replace
(
"-"
,
"_"
)
)
egg_info
.
mkdir
(
exist_ok
=
True
)
with
egg_info
.
joinpath
(
"PKG-INFO"
)
.
open
(
"w"
,
encoding
=
"utf-8"
)
as
f
:
f
.
write
(
decode
(
self
.
get_metadata_content
()))
with
egg_info
.
joinpath
(
"entry_points.txt"
)
.
open
(
"w"
,
encoding
=
"utf-8"
)
as
f
:
entry_points
=
self
.
convert_entry_points
()
for
group_name
in
sorted
(
entry_points
):
f
.
write
(
"[{}]
\n
"
.
format
(
group_name
))
for
ep
in
sorted
(
entry_points
[
group_name
]):
f
.
write
(
ep
.
replace
(
" "
,
""
)
+
"
\n
"
)
f
.
write
(
"
\n
"
)
with
egg_info
.
joinpath
(
"requires.txt"
)
.
open
(
"w"
,
encoding
=
"utf-8"
)
as
f
:
f
.
write
(
self
.
_generate_requires
())
def
_build_egg_link
(
self
):
egg_link
=
self
.
_env
.
site_packages
/
"{}.egg-link"
.
format
(
self
.
_package
.
name
)
with
egg_link
.
open
(
"w"
,
encoding
=
"utf-8"
)
as
f
:
f
.
write
(
str
(
self
.
_poetry
.
file
.
parent
.
resolve
())
+
"
\n
"
)
f
.
write
(
"."
)
def
_add_easy_install_entry
(
self
):
easy_install_pth
=
self
.
_env
.
site_packages
/
"easy-install.pth"
path
=
str
(
self
.
_poetry
.
file
.
parent
.
resolve
())
content
=
""
if
easy_install_pth
.
exists
():
with
easy_install_pth
.
open
(
encoding
=
"utf-8"
)
as
f
:
content
=
f
.
read
()
if
path
in
content
:
return
content
+=
"{}
\n
"
.
format
(
path
)
with
easy_install_pth
.
open
(
"w"
,
encoding
=
"utf-8"
)
as
f
:
f
.
write
(
content
)
def
_generate_requires
(
self
):
extras
=
defaultdict
(
list
)
requires
=
""
for
dep
in
sorted
(
self
.
_package
.
requires
,
key
=
lambda
d
:
d
.
name
):
marker
=
dep
.
marker
if
marker
.
is_any
():
requires
+=
"{}
\n
"
.
format
(
dep
.
base_pep_508_name
)
continue
extras
[
str
(
marker
)]
.
append
(
dep
.
base_pep_508_name
)
if
extras
:
requires
+=
"
\n
"
for
marker
,
deps
in
sorted
(
extras
.
items
()):
requires
+=
"[:{}]
\n
"
.
format
(
marker
)
for
dep
in
deps
:
requires
+=
dep
+
"
\n
"
requires
+=
"
\n
"
return
requires
poetry/utils/env.py
View file @
2ed53be4
import
json
import
os
import
platform
import
re
import
subprocess
import
sys
import
sysconfig
...
...
@@ -15,6 +16,7 @@ from typing import Tuple
from
poetry.config
import
Config
from
poetry.locations
import
CACHE_DIR
from
poetry.semver.version
import
Version
from
poetry.utils._compat
import
Path
from
poetry.utils._compat
import
decode
from
poetry.utils._compat
import
encode
...
...
@@ -114,6 +116,7 @@ class Env(object):
self
.
_base
=
base
or
path
self
.
_marker_env
=
None
self
.
_pip_version
=
None
@property
def
path
(
self
):
# type: () -> Path
...
...
@@ -152,6 +155,25 @@ class Env(object):
"""
return
self
.
_bin
(
"pip"
)
@property
def
pip_version
(
self
):
if
self
.
_pip_version
is
None
:
self
.
_pip_version
=
self
.
get_pip_version
()
return
self
.
_pip_version
@property
def
site_packages
(
self
):
# type: () -> Path
if
self
.
_is_windows
:
return
self
.
_path
/
"Lib"
/
"site-packages"
return
(
self
.
_path
/
"lib"
/
"python{}.{}"
.
format
(
*
self
.
version_info
[:
2
])
/
"site-packages"
)
@classmethod
def
get
(
cls
,
cwd
,
reload
=
False
):
# type: (Path, bool) -> Env
if
cls
.
_env
is
not
None
and
not
reload
:
...
...
@@ -308,6 +330,9 @@ class Env(object):
def
config_var
(
self
,
var
):
# type: (str) -> Any
raise
NotImplementedError
()
def
get_pip_version
(
self
):
# type: () -> Version
raise
NotImplementedError
()
def
is_valid_for_marker
(
self
,
marker
):
# type: (BaseMarker) -> bool
return
marker
.
validate
(
self
.
marker_env
)
...
...
@@ -424,6 +449,11 @@ class SystemEnv(Env):
return
def
get_pip_version
(
self
):
# type: () -> Version
from
pip
import
__version__
return
Version
.
parse
(
__version__
)
def
is_venv
(
self
):
# type: () -> bool
return
self
.
_path
!=
self
.
_base
...
...
@@ -474,6 +504,14 @@ class VirtualEnv(Env):
return
value
def
get_pip_version
(
self
):
# type: () -> Version
output
=
self
.
run
(
"python"
,
"-m"
,
"pip"
,
"--version"
)
.
strip
()
m
=
re
.
match
(
"pip (.+?)(?: from .+)?$"
,
output
)
if
not
m
:
return
Version
.
parse
(
"0.0"
)
return
Version
.
parse
(
m
.
group
(
1
))
def
is_venv
(
self
):
# type: () -> bool
return
True
...
...
@@ -546,6 +584,7 @@ class MockEnv(NullEnv):
platform
=
"darwin"
,
os_name
=
"posix"
,
is_venv
=
False
,
pip_version
=
"19.1"
,
**
kwargs
):
super
(
MockEnv
,
self
)
.
__init__
(
**
kwargs
)
...
...
@@ -555,6 +594,7 @@ class MockEnv(NullEnv):
self
.
_platform
=
platform
self
.
_os_name
=
os_name
self
.
_is_venv
=
is_venv
self
.
_pip_version
=
Version
.
parse
(
pip_version
)
@property
def
version_info
(
self
):
# type: () -> Tuple[int]
...
...
@@ -572,5 +612,9 @@ class MockEnv(NullEnv):
def
os
(
self
):
# type: () -> str
return
self
.
_os_name
@property
def
pip_version
(
self
):
return
self
.
_pip_version
def
is_venv
(
self
):
# type: () -> bool
return
self
.
_is_venv
tests/masonry/builders/test_editable.py
0 → 100644
View file @
2ed53be4
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
poetry.io
import
NullIO
from
poetry.masonry.builders
import
EditableBuilder
from
poetry.poetry
import
Poetry
from
poetry.utils._compat
import
Path
from
poetry.utils.env
import
MockEnv
fixtures_dir
=
Path
(
__file__
)
.
parent
/
"fixtures"
def
test_build_pure_python_package
(
tmp_dir
):
tmp_dir
=
Path
(
tmp_dir
)
env
=
MockEnv
(
path
=
tmp_dir
)
env
.
site_packages
.
mkdir
(
parents
=
True
)
module_path
=
fixtures_dir
/
"complete"
builder
=
EditableBuilder
(
Poetry
.
create
(
module_path
),
env
,
NullIO
())
builder
.
_path
=
tmp_dir
builder
.
build
()
egg_info
=
tmp_dir
/
"my_package.egg-info"
assert
egg_info
.
exists
()
entry_points
=
"""
\
[console_scripts]
extra-script=my_package.extra:main[time]
my-2nd-script=my_package:main2
my-script=my_package:main
"""
pkg_info
=
"""
\
Metadata-Version: 2.1
Name: my-package
Version: 1.2.3
Summary: Some description.
Home-page: https://poetry.eustace.io/
License: MIT
Keywords: packaging,dependency,poetry
Author: Sébastien Eustace
Author-email: sebastien@eustace.io
Requires-Python: >=3.6,<4.0
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Provides-Extra: time
Requires-Dist: cachy[msgpack] (>=0.2.0,<0.3.0)
Requires-Dist: cleo (>=0.6,<0.7)
Requires-Dist: pendulum (>=1.4,<2.0); extra == "time"
Project-URL: Documentation, https://poetry.eustace.io/docs
Project-URL: Repository, https://github.com/sdispater/poetry
Description-Content-Type: text/x-rst
My Package
==========
"""
requires
=
"""
\
cachy[msgpack] (>=0.2.0,<0.3.0)
cleo (>=0.6,<0.7)
pendulum (>=1.4,<2.0)
"""
with
egg_info
.
joinpath
(
"entry_points.txt"
)
.
open
(
encoding
=
"utf-8"
)
as
f
:
assert
entry_points
==
f
.
read
()
with
egg_info
.
joinpath
(
"PKG-INFO"
)
.
open
(
encoding
=
"utf-8"
)
as
f
:
assert
pkg_info
==
f
.
read
()
with
egg_info
.
joinpath
(
"requires.txt"
)
.
open
(
encoding
=
"utf-8"
)
as
f
:
assert
requires
==
f
.
read
()
egg_link
=
env
.
site_packages
/
"my-package.egg-link"
with
egg_link
.
open
(
encoding
=
"utf-8"
)
as
f
:
assert
str
(
module_path
)
+
"
\n
."
==
f
.
read
()
easy_install
=
env
.
site_packages
/
"easy-install.pth"
with
easy_install
.
open
(
encoding
=
"utf-8"
)
as
f
:
assert
str
(
module_path
)
+
"
\n
"
in
f
.
readlines
()
def
test_build_should_delegate_to_pip_for_non_pure_python_packages
(
tmp_dir
,
mocker
):
move
=
mocker
.
patch
(
"shutil.move"
)
tmp_dir
=
Path
(
tmp_dir
)
env
=
MockEnv
(
path
=
tmp_dir
,
pip_version
=
"18.1"
,
execute
=
False
)
env
.
site_packages
.
mkdir
(
parents
=
True
)
module_path
=
fixtures_dir
/
"extended"
builder
=
EditableBuilder
(
Poetry
.
create
(
module_path
),
env
,
NullIO
())
builder
.
build
()
expected
=
[[
"python"
,
"-m"
,
"pip"
,
"install"
,
"-e"
,
str
(
module_path
)]]
assert
expected
==
env
.
executed
assert
0
==
move
.
call_count
def
test_build_should_temporarily_remove_the_pyproject_file
(
tmp_dir
,
mocker
):
move
=
mocker
.
patch
(
"shutil.move"
)
tmp_dir
=
Path
(
tmp_dir
)
env
=
MockEnv
(
path
=
tmp_dir
,
pip_version
=
"19.1"
,
execute
=
False
)
env
.
site_packages
.
mkdir
(
parents
=
True
)
module_path
=
fixtures_dir
/
"extended"
builder
=
EditableBuilder
(
Poetry
.
create
(
module_path
),
env
,
NullIO
())
builder
.
build
()
expected
=
[[
"python"
,
"-m"
,
"pip"
,
"install"
,
"-e"
,
str
(
module_path
)]]
assert
expected
==
env
.
executed
assert
2
==
move
.
call_count
expected_calls
=
[
mocker
.
call
(
str
(
module_path
/
"pyproject.toml"
),
str
(
module_path
/
"pyproject.tmp"
)
),
mocker
.
call
(
str
(
module_path
/
"pyproject.tmp"
),
str
(
module_path
/
"pyproject.toml"
)
),
]
assert
expected_calls
==
move
.
call_args_list
tests/masonry/builders/test_wheel.py
View file @
2ed53be4
...
...
@@ -3,15 +3,11 @@ import pytest
import
shutil
import
zipfile
from
email.parser
import
Parser
from
poetry.io
import
NullIO
from
poetry.masonry.builders
import
WheelBuilder
from
poetry.poetry
import
Poetry
from
poetry.utils._compat
import
Path
from
poetry.utils._compat
import
to_str
from
poetry.utils.env
import
NullEnv
from
poetry.packages
import
ProjectPackage
fixtures_dir
=
Path
(
__file__
)
.
parent
/
"fixtures"
...
...
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