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
54c82eeb
Unverified
Commit
54c82eeb
authored
Feb 27, 2018
by
Sébastien Eustace
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add the add command
parent
2c53da3f
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
313 additions
and
7 deletions
+313
-7
poetry/console/application.py
+5
-0
poetry/console/commands/__init__.py
+1
-0
poetry/console/commands/add.py
+175
-0
poetry/console/commands/command.py
+3
-0
poetry/installation/installer.py
+31
-2
poetry/packages/locker.py
+8
-0
poetry/poetry.py
+9
-3
poetry/puzzle/solver.py
+9
-2
poetry/utils/toml_file.py
+4
-0
poetry/version/__init__.py
+0
-0
poetry/version/version_selector.py
+68
-0
No files found.
poetry/console/application.py
View file @
54c82eeb
...
@@ -6,6 +6,7 @@ from poetry.poetry import Poetry
...
@@ -6,6 +6,7 @@ from poetry.poetry import Poetry
from
poetry.utils.venv
import
Venv
from
poetry.utils.venv
import
Venv
from
.commands
import
AboutCommand
from
.commands
import
AboutCommand
from
.commands
import
AddCommand
from
.commands
import
InstallCommand
from
.commands
import
InstallCommand
from
.commands
import
LockCommand
from
.commands
import
LockCommand
from
.commands
import
UpdateCommand
from
.commands
import
UpdateCommand
...
@@ -28,6 +29,9 @@ class Application(BaseApplication):
...
@@ -28,6 +29,9 @@ class Application(BaseApplication):
return
self
.
_poetry
return
self
.
_poetry
def
reset_poetry
(
self
)
->
None
:
self
.
_poetry
=
None
@property
@property
def
venv
(
self
)
->
Venv
:
def
venv
(
self
)
->
Venv
:
return
self
.
_venv
return
self
.
_venv
...
@@ -37,6 +41,7 @@ class Application(BaseApplication):
...
@@ -37,6 +41,7 @@ class Application(BaseApplication):
return
commands
+
[
return
commands
+
[
AboutCommand
(),
AboutCommand
(),
AddCommand
(),
InstallCommand
(),
InstallCommand
(),
LockCommand
(),
LockCommand
(),
UpdateCommand
(),
UpdateCommand
(),
...
...
poetry/console/commands/__init__.py
View file @
54c82eeb
from
.about
import
AboutCommand
from
.about
import
AboutCommand
from
.add
import
AddCommand
from
.install
import
InstallCommand
from
.install
import
InstallCommand
from
.lock
import
LockCommand
from
.lock
import
LockCommand
from
.update
import
UpdateCommand
from
.update
import
UpdateCommand
poetry/console/commands/add.py
0 → 100644
View file @
54c82eeb
import
re
from
typing
import
List
from
typing
import
Tuple
from
poetry.installation
import
Installer
from
poetry.semver.version_parser
import
VersionParser
from
poetry.version.version_selector
import
VersionSelector
from
.command
import
Command
class
AddCommand
(
Command
):
"""
Add a new depdency to <comment>poetry.toml</>.
add
{ name* : Packages to add. }
{--D|dev : Add package as development dependency. }
{--optional : Add as an optional dependency. }
"""
help
=
"""The add command adds required packages to your <comment>poetry.toml</> and installs them.
If you do not specify a version constraint, poetry will choose a suitable one based on the available package versions.
"""
def
handle
(
self
):
names
=
self
.
argument
(
'name'
)
is_dev
=
self
.
option
(
'dev'
)
requirements
=
self
.
_determine_requirements
(
names
)
requirements
=
self
.
_format_requirements
(
requirements
)
# validate requirements format
parser
=
VersionParser
()
for
constraint
in
requirements
.
values
():
parser
.
parse_constraints
(
constraint
)
# Trying to figure out where to add our dependencies
# If we find a toml library that keeps comments
# We could remove this whole section
section
=
'[dependencies]'
if
is_dev
:
section
=
'[dev-dependencies]'
new_content
=
None
with
self
.
poetry
.
locker
.
original
.
path
.
open
()
as
fd
:
content
=
fd
.
read
()
.
split
(
'
\n
'
)
in_section
=
False
index
=
None
for
i
,
line
in
enumerate
(
content
):
line
=
line
.
strip
()
if
line
==
section
:
in_section
=
True
continue
if
in_section
and
not
line
:
index
=
i
break
if
index
is
not
None
:
for
i
,
require
in
enumerate
(
requirements
.
items
()):
name
,
version
=
require
content
.
insert
(
index
+
i
,
f
'{name} = "{version}"'
)
new_content
=
'
\n
'
.
join
(
content
)
if
new_content
is
not
None
:
with
self
.
poetry
.
locker
.
original
.
path
.
open
(
'w'
)
as
fd
:
fd
.
write
(
new_content
)
else
:
# We could not find where to put the dependencies
# We raise an warning
self
.
warning
(
'Unable to automatically add dependencies'
)
self
.
warning
(
'Add them manually to your poetry.toml'
)
return
1
# Update packages
self
.
reset_poetry
()
installer
=
Installer
(
self
.
output
,
self
.
poetry
.
package
,
self
.
poetry
.
locker
,
self
.
poetry
.
repository
)
installer
.
update
(
True
)
installer
.
whitelist
(
requirements
)
installer
.
run
()
def
_determine_requirements
(
self
,
requires
:
List
[
str
])
->
List
[
str
]:
if
not
requires
:
return
[]
requires
=
self
.
_parse_name_version_pairs
(
requires
)
result
=
[]
for
requirement
in
requires
:
if
'version'
not
in
requirement
:
# determine the best version automatically
name
,
version
=
self
.
_find_best_version_for_package
(
requirement
[
'name'
]
)
requirement
[
'version'
]
=
version
requirement
[
'name'
]
=
name
self
.
line
(
f
'Using version <info>{version}</> for <info>{name}</>'
)
else
:
# check that the specified version/constraint exists
# before we proceed
name
,
_
=
self
.
_find_best_version_for_package
(
requirement
[
'name'
],
requirement
[
'version'
]
)
requirement
[
'name'
]
=
name
result
.
append
(
f
'{requirement["name"]} {requirement["version"]}'
)
return
result
def
_find_best_version_for_package
(
self
,
name
,
required_version
=
None
)
->
Tuple
[
str
,
str
]:
selector
=
VersionSelector
(
self
.
poetry
.
repository
)
package
=
selector
.
find_best_candidate
(
name
,
required_version
)
if
not
package
:
# TODO: find similar
raise
ValueError
(
f
'Could not find a matching version of package {name}'
)
return
(
package
.
pretty_name
,
selector
.
find_recommended_require_version
(
package
)
)
def
_parse_name_version_pairs
(
self
,
pairs
:
list
)
->
list
:
result
=
[]
for
i
in
range
(
len
(
pairs
)):
pair
=
re
.
sub
(
'^([^=: ]+)[=: ](.*)$'
,
'
\\
1
\\
2'
,
pairs
[
i
]
.
strip
())
pair
=
pair
.
strip
()
if
' '
in
pair
:
name
,
version
=
pair
.
split
(
' '
,
2
)
result
.
append
({
'name'
:
name
,
'version'
:
version
})
else
:
result
.
append
({
'name'
:
pair
})
return
result
def
_format_requirements
(
self
,
requirements
:
List
[
str
])
->
dict
:
requires
=
{}
requirements
=
self
.
_parse_name_version_pairs
(
requirements
)
for
requirement
in
requirements
:
requires
[
requirement
[
'name'
]]
=
requirement
[
'version'
]
return
requires
poetry/console/commands/command.py
View file @
54c82eeb
...
@@ -11,6 +11,9 @@ class Command(BaseCommand):
...
@@ -11,6 +11,9 @@ class Command(BaseCommand):
def
poetry
(
self
)
->
Poetry
:
def
poetry
(
self
)
->
Poetry
:
return
self
.
get_application
()
.
poetry
return
self
.
get_application
()
.
poetry
def
reset_poetry
(
self
)
->
None
:
self
.
get_application
()
.
reset_poetry
()
def
run
(
self
,
i
,
o
)
->
int
:
def
run
(
self
,
i
,
o
)
->
int
:
"""
"""
Initialize command.
Initialize command.
...
...
poetry/installation/installer.py
View file @
54c82eeb
from
typing
import
List
from
typing
import
List
from
poetry.packages
import
Dependency
from
poetry.packages
import
Locker
from
poetry.packages
import
Locker
from
poetry.packages
import
Package
from
poetry.packages
import
Package
from
poetry.puzzle
import
Solver
from
poetry.puzzle
import
Solver
...
@@ -32,6 +33,8 @@ class Installer:
...
@@ -32,6 +33,8 @@ class Installer:
self
.
_dev_mode
=
True
self
.
_dev_mode
=
True
self
.
_execute_operations
=
True
self
.
_execute_operations
=
True
self
.
_whitelist
=
{}
self
.
_installer
=
PipInstaller
(
self
.
_io
.
venv
,
self
.
_io
)
self
.
_installer
=
PipInstaller
(
self
.
_io
.
venv
,
self
.
_io
)
def
run
(
self
):
def
run
(
self
):
...
@@ -86,6 +89,11 @@ class Installer:
...
@@ -86,6 +89,11 @@ class Installer:
return
self
return
self
def
whitelist
(
self
,
packages
:
dict
)
->
'Installer'
:
self
.
_whitelist
=
packages
return
self
def
_do_install
(
self
,
local_repo
):
def
_do_install
(
self
,
local_repo
):
locked_repository
=
Repository
()
locked_repository
=
Repository
()
# initialize locked repo if we are installing from lock
# initialize locked repo if we are installing from lock
...
@@ -94,6 +102,27 @@ class Installer:
...
@@ -94,6 +102,27 @@ class Installer:
if
self
.
_update
:
if
self
.
_update
:
self
.
_io
.
writeln
(
'<info>Updating dependencies</>'
)
self
.
_io
.
writeln
(
'<info>Updating dependencies</>'
)
fixed
=
[]
# If the whitelist is enabled, packages not in it are fixed
# to the version specified in the lock
if
self
.
_whitelist
:
# collect packages to fixate from root requirements
candidates
=
[]
for
package
in
locked_repository
.
packages
:
candidates
.
append
(
package
)
# fix them to the version in lock if they are not updateable
for
candidate
in
candidates
:
to_fix
=
True
for
require
in
self
.
_whitelist
.
keys
():
if
require
==
candidate
.
name
:
to_fix
=
False
if
to_fix
:
fixed
.
append
(
Dependency
(
candidate
.
name
,
candidate
.
version
)
)
solver
=
Solver
(
locked_repository
,
self
.
_io
)
solver
=
Solver
(
locked_repository
,
self
.
_io
)
...
@@ -101,7 +130,7 @@ class Installer:
...
@@ -101,7 +130,7 @@ class Installer:
if
self
.
is_dev_mode
():
if
self
.
is_dev_mode
():
request
+=
self
.
_package
.
dev_requires
request
+=
self
.
_package
.
dev_requires
ops
=
solver
.
solve
(
request
,
self
.
_repository
)
ops
=
solver
.
solve
(
request
,
self
.
_repository
,
fixed
=
fixed
)
else
:
else
:
self
.
_io
.
writeln
(
'<info>Installing dependencies from lock file</>'
)
self
.
_io
.
writeln
(
'<info>Installing dependencies from lock file</>'
)
# If we are installing from lock
# If we are installing from lock
...
@@ -112,7 +141,7 @@ class Installer:
...
@@ -112,7 +141,7 @@ class Installer:
self
.
_io
.
new_line
()
self
.
_io
.
new_line
()
# Execute operations
# Execute operations
if
not
ops
and
self
.
_execute_operations
:
if
not
ops
and
(
self
.
_execute_operations
or
self
.
_dry_run
)
:
self
.
_io
.
writeln
(
'Nothing to install or update'
)
self
.
_io
.
writeln
(
'Nothing to install or update'
)
if
ops
and
(
self
.
_execute_operations
or
self
.
_dry_run
):
if
ops
and
(
self
.
_execute_operations
or
self
.
_dry_run
):
...
...
poetry/packages/locker.py
View file @
54c82eeb
...
@@ -27,6 +27,14 @@ class Locker:
...
@@ -27,6 +27,14 @@ class Locker:
self
.
_content_hash
=
self
.
_get_content_hash
()
self
.
_content_hash
=
self
.
_get_content_hash
()
@property
@property
def
original
(
self
)
->
TomlFile
:
return
self
.
_original
@property
def
lock
(
self
)
->
TomlFile
:
return
self
.
_lock
@property
def
lock_data
(
self
):
def
lock_data
(
self
):
if
self
.
_lock_data
is
None
:
if
self
.
_lock_data
is
None
:
self
.
_lock_data
=
self
.
_get_lock_data
()
self
.
_lock_data
=
self
.
_get_lock_data
()
...
...
poetry/poetry.py
View file @
54c82eeb
...
@@ -2,6 +2,7 @@ from pathlib import Path
...
@@ -2,6 +2,7 @@ from pathlib import Path
from
.packages
import
Locker
from
.packages
import
Locker
from
.packages
import
Package
from
.packages
import
Package
from
.repositories.pypi_repository
import
PyPiRepository
from
.semver.helpers
import
normalize_version
from
.semver.helpers
import
normalize_version
from
.utils.toml_file
import
TomlFile
from
.utils.toml_file
import
TomlFile
...
@@ -17,19 +18,24 @@ class Poetry:
...
@@ -17,19 +18,24 @@ class Poetry:
self
.
_package
=
package
self
.
_package
=
package
self
.
_config
=
config
self
.
_config
=
config
self
.
_locker
=
locker
self
.
_locker
=
locker
self
.
_repository
=
PyPiRepository
()
@property
@property
def
package
(
self
):
def
package
(
self
)
->
Package
:
return
self
.
_package
return
self
.
_package
@property
@property
def
config
(
self
):
def
config
(
self
)
->
dict
:
return
self
.
_config
return
self
.
_config
@property
@property
def
locker
(
self
):
def
locker
(
self
)
->
Locker
:
return
self
.
_locker
return
self
.
_locker
@property
def
repository
(
self
)
->
PyPiRepository
:
return
self
.
_repository
@classmethod
@classmethod
def
create
(
cls
,
cwd
)
->
'Poetry'
:
def
create
(
cls
,
cwd
)
->
'Poetry'
:
poetry_file
=
Path
(
cwd
)
/
'poetry.toml'
poetry_file
=
Path
(
cwd
)
/
'poetry.toml'
...
...
poetry/puzzle/solver.py
View file @
54c82eeb
from
typing
import
List
from
typing
import
List
from
poetry.mixology
import
Resolver
from
poetry.mixology
import
Resolver
from
poetry.mixology.dependency_graph
import
DependencyGraph
from
poetry.mixology.exceptions
import
ResolverError
from
poetry.mixology.exceptions
import
ResolverError
from
.exceptions
import
SolverProblemError
from
.exceptions
import
SolverProblemError
...
@@ -19,11 +20,17 @@ class Solver:
...
@@ -19,11 +20,17 @@ class Solver:
self
.
_installed
=
installed
self
.
_installed
=
installed
self
.
_io
=
io
self
.
_io
=
io
def
solve
(
self
,
requested
,
repository
)
->
List
[
Operation
]:
def
solve
(
self
,
requested
,
repository
,
fixed
=
None
)
->
List
[
Operation
]:
resolver
=
Resolver
(
Provider
(
repository
),
UI
(
self
.
_io
))
resolver
=
Resolver
(
Provider
(
repository
),
UI
(
self
.
_io
))
base
=
None
if
fixed
is
not
None
:
base
=
DependencyGraph
()
for
fixed_req
in
fixed
:
base
.
add_vertex
(
fixed_req
.
name
,
fixed_req
,
True
)
try
:
try
:
graph
=
resolver
.
resolve
(
requested
)
graph
=
resolver
.
resolve
(
requested
,
base
=
base
)
except
ResolverError
as
e
:
except
ResolverError
as
e
:
raise
SolverProblemError
(
e
)
raise
SolverProblemError
(
e
)
...
...
poetry/utils/toml_file.py
View file @
54c82eeb
...
@@ -9,6 +9,10 @@ class TomlFile:
...
@@ -9,6 +9,10 @@ class TomlFile:
def
__init__
(
self
,
path
):
def
__init__
(
self
,
path
):
self
.
_path
=
Path
(
path
)
self
.
_path
=
Path
(
path
)
@property
def
path
(
self
):
return
self
.
_path
def
read
(
self
)
->
dict
:
def
read
(
self
)
->
dict
:
return
loads
(
self
.
_path
.
read_text
())
return
loads
(
self
.
_path
.
read_text
())
...
...
poetry/version/__init__.py
0 → 100644
View file @
54c82eeb
poetry/version/version_selector.py
0 → 100644
View file @
54c82eeb
import
re
from
typing
import
Union
from
poetry.packages
import
Package
from
poetry.semver.helpers
import
normalize_version
from
poetry.semver.version_parser
import
VersionParser
class
VersionSelector
(
object
):
def
__init__
(
self
,
repository
,
parser
=
VersionParser
()):
self
.
_repository
=
repository
self
.
_parser
=
parser
def
find_best_candidate
(
self
,
package_name
:
str
,
target_package_version
:
Union
[
str
,
None
]
=
None
)
->
Union
[
Package
,
bool
]:
"""
Given a package name and optional version,
returns the latest Package that matches
"""
if
target_package_version
:
constraint
=
self
.
_parser
.
parse_constraints
(
target_package_version
)
else
:
constraint
=
None
candidates
=
self
.
_repository
.
find_packages
(
package_name
,
constraint
)
if
not
candidates
:
return
False
# Select highest version if we have many
package
=
candidates
[
0
]
for
candidate
in
candidates
:
# Select highest version of the two
if
package
.
version
<
candidate
.
version
:
package
=
candidate
return
package
def
find_recommended_require_version
(
self
,
package
):
version
=
package
.
version
return
self
.
_transform_version
(
version
,
package
.
pretty_version
)
def
_transform_version
(
self
,
version
,
pretty_version
):
# attempt to transform 2.1.1 to 2.1
# this allows you to upgrade through minor versions
try
:
parts
=
normalize_version
(
version
)
.
split
(
'.'
)
except
ValueError
:
return
pretty_version
# check to see if we have a semver-looking version
if
len
(
parts
)
==
4
and
re
.
match
(
'^0
\
D?'
,
parts
[
3
]):
# remove the last parts (the patch version number and any extra)
if
parts
[
0
]
==
'0'
:
del
parts
[
3
]
else
:
del
parts
[
3
]
del
parts
[
2
]
version
=
'.'
.
join
(
parts
)
else
:
return
pretty_version
return
f
'^{version}'
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