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
7a14aa59
Unverified
Commit
7a14aa59
authored
Apr 30, 2018
by
Sébastien Eustace
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add support for git dependencies in the add command
parent
9cf87a28
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
275 additions
and
123 deletions
+275
-123
CHANGELOG.md
+1
-0
poetry/console/commands/add.py
+40
-24
poetry/puzzle/provider.py
+22
-8
poetry/repositories/pypi_repository.py
+2
-42
poetry/utils/helpers.py
+43
-0
tests/console/commands/test_add.py
+73
-0
tests/console/conftest.py
+29
-0
tests/fixtures/git/github.com/demo/demo/demo/__init__.py
+0
-0
tests/fixtures/git/github.com/demo/pyproject-demo/demo/__init__.py
+0
-0
tests/fixtures/git/github.com/demo/pyproject-demo/pyproject.toml
+15
-0
tests/repositories/test_pypi_repository.py
+0
-49
tests/utils/test_helpers.py
+50
-0
No files found.
CHANGELOG.md
View file @
7a14aa59
...
@@ -5,6 +5,7 @@
...
@@ -5,6 +5,7 @@
### Added
### Added
-
Added the
`cache:clear`
command.
-
Added the
`cache:clear`
command.
-
Added support for
`git`
dependencies in the
`add`
command.
### Changed
### Changed
...
...
poetry/console/commands/add.py
View file @
7a14aa59
...
@@ -12,10 +12,11 @@ class AddCommand(VenvCommand):
...
@@ -12,10 +12,11 @@ class AddCommand(VenvCommand):
add
add
{ name* : Packages to add. }
{ name* : Packages to add. }
{--D|dev : Add package as development dependency. }
{ --D|dev : Add package as development dependency. }
{--optional : Add as an optional dependency. }
{ --git= : The url of the Git repository. }
{ --optional : Add as an optional dependency. }
{ --allow-prereleases : Accept prereleases. }
{ --allow-prereleases : Accept prereleases. }
{--dry-run : Outputs the operations but will not execute anything
{
--dry-run : Outputs the operations but will not execute anything
(implicitly enables --verbose). }
(implicitly enables --verbose). }
"""
"""
...
@@ -35,6 +36,11 @@ If you do not specify a version constraint, poetry will choose a suitable one ba
...
@@ -35,6 +36,11 @@ If you do not specify a version constraint, poetry will choose a suitable one ba
packages
=
self
.
argument
(
'name'
)
packages
=
self
.
argument
(
'name'
)
is_dev
=
self
.
option
(
'dev'
)
is_dev
=
self
.
option
(
'dev'
)
if
self
.
option
(
'git'
)
and
len
(
packages
)
>
1
:
raise
ValueError
(
'You can only specify one package when using the --git option'
)
section
=
'dependencies'
section
=
'dependencies'
if
is_dev
:
if
is_dev
:
section
=
'dev-dependencies'
section
=
'dev-dependencies'
...
@@ -50,30 +56,40 @@ If you do not specify a version constraint, poetry will choose a suitable one ba
...
@@ -50,30 +56,40 @@ If you do not specify a version constraint, poetry will choose a suitable one ba
'Package {} is already present'
.
format
(
name
)
'Package {} is already present'
.
format
(
name
)
)
)
requirements
=
self
.
_determine_requirements
(
if
self
.
option
(
'git'
):
packages
,
requirements
=
{
allow_prereleases
=
self
.
option
(
'allow-prereleases'
)
packages
[
0
]:
''
)
}
requirements
=
self
.
_format_requirements
(
requirements
)
else
:
requirements
=
self
.
_determine_requirements
(
packages
,
allow_prereleases
=
self
.
option
(
'allow-prereleases'
)
)
requirements
=
self
.
_format_requirements
(
requirements
)
# validate requirements format
# validate requirements format
parser
=
VersionParser
()
parser
=
VersionParser
()
for
constraint
in
requirements
.
values
():
for
constraint
in
requirements
.
values
():
parser
.
parse_constraints
(
constraint
)
parser
.
parse_constraints
(
constraint
)
for
name
,
constraint
in
requirements
.
items
():
for
name
,
constraint
in
requirements
.
items
():
if
self
.
option
(
'optional'
)
or
self
.
option
(
'allow-prereleases'
):
constraint
=
{
constraint
=
{
'version'
:
constraint
'version'
:
constraint
}
}
if
self
.
option
(
'git'
):
if
self
.
option
(
'optional'
):
del
constraint
[
'version'
]
constraint
=
{
'optional'
:
True
constraint
[
'git'
]
=
self
.
option
(
'git'
)
}
if
self
.
option
(
'optional'
):
if
self
.
option
(
'allow-prereleases'
):
constraint
[
'optional'
]
=
True
constraint
[
'allows-prereleases'
]
=
True
if
self
.
option
(
'allow-prereleases'
):
constraint
[
'allows-prereleases'
]
=
True
if
len
(
constraint
)
==
1
and
'version'
in
constraint
:
constraint
=
constraint
[
'version'
]
poetry_content
[
section
][
name
]
=
constraint
poetry_content
[
section
][
name
]
=
constraint
...
...
poetry/puzzle/provider.py
View file @
7a14aa59
import
os
import
os
import
pkginfo
import
shutil
import
shutil
from
functools
import
cmp_to_key
from
functools
import
cmp_to_key
...
@@ -22,6 +23,7 @@ from poetry.repositories import Pool
...
@@ -22,6 +23,7 @@ from poetry.repositories import Pool
from
poetry.semver
import
less_than
from
poetry.semver
import
less_than
from
poetry.utils._compat
import
Path
from
poetry.utils._compat
import
Path
from
poetry.utils.helpers
import
parse_requires
from
poetry.utils.toml_file
import
TomlFile
from
poetry.utils.toml_file
import
TomlFile
from
poetry.utils.venv
import
Venv
from
poetry.utils.venv
import
Venv
...
@@ -175,15 +177,27 @@ class Provider(SpecificationProvider, UI):
...
@@ -175,15 +177,27 @@ class Provider(SpecificationProvider, UI):
try
:
try
:
venv
=
Venv
.
create
(
self
.
_io
)
venv
=
Venv
.
create
(
self
.
_io
)
output
=
venv
.
run
(
venv
.
run
(
'python'
,
'setup.py'
,
'python'
,
'setup.py'
,
'egg_info'
'--name'
,
'--version'
)
)
output
=
output
.
split
(
'
\n
'
)
name
=
output
[
-
3
]
egg_info
=
list
(
tmp_dir
.
glob
(
'*.egg-info'
))[
0
]
version
=
output
[
-
2
]
package
=
Package
(
name
,
version
,
version
)
meta
=
pkginfo
.
UnpackedSDist
(
str
(
egg_info
))
# Figure out a way to get requirements
if
meta
.
requires_dist
:
reqs
=
list
(
meta
.
requires_dist
)
else
:
reqs
=
[]
requires
=
egg_info
/
'requires.txt'
if
requires
.
exists
():
with
requires
.
open
()
as
f
:
reqs
=
parse_requires
(
f
.
read
())
package
=
Package
(
meta
.
name
,
meta
.
version
)
for
req
in
reqs
:
package
.
requires
.
append
(
dependency_from_pep_508
(
req
))
except
Exception
:
except
Exception
:
raise
raise
finally
:
finally
:
...
...
poetry/repositories/pypi_repository.py
View file @
7a14aa59
...
@@ -34,6 +34,7 @@ from poetry.semver.constraints.base_constraint import BaseConstraint
...
@@ -34,6 +34,7 @@ from poetry.semver.constraints.base_constraint import BaseConstraint
from
poetry.semver.version_parser
import
VersionParser
from
poetry.semver.version_parser
import
VersionParser
from
poetry.utils._compat
import
Path
from
poetry.utils._compat
import
Path
from
poetry.utils._compat
import
to_str
from
poetry.utils._compat
import
to_str
from
poetry.utils.helpers
import
parse_requires
from
poetry.utils.helpers
import
temporary_directory
from
poetry.utils.helpers
import
temporary_directory
from
poetry.version.markers
import
InvalidMarker
from
poetry.version.markers
import
InvalidMarker
...
@@ -424,7 +425,7 @@ class PyPiRepository(Repository):
...
@@ -424,7 +425,7 @@ class PyPiRepository(Repository):
requires
=
egg_info
/
'requires.txt'
requires
=
egg_info
/
'requires.txt'
if
requires
.
exists
():
if
requires
.
exists
():
with
requires
.
open
()
as
f
:
with
requires
.
open
()
as
f
:
return
self
.
_
parse_requires
(
f
.
read
())
return
parse_requires
(
f
.
read
())
return
return
...
@@ -442,46 +443,5 @@ class PyPiRepository(Repository):
...
@@ -442,46 +443,5 @@ class PyPiRepository(Repository):
if
chunk
:
if
chunk
:
f
.
write
(
chunk
)
f
.
write
(
chunk
)
def
_parse_requires
(
self
,
requires
):
# type: (str) -> Union[list, None]
lines
=
requires
.
split
(
'
\n
'
)
requires_dist
=
[]
in_section
=
False
current_marker
=
None
for
line
in
lines
:
line
=
line
.
strip
()
if
not
line
:
if
in_section
:
in_section
=
False
continue
if
line
.
startswith
(
'['
):
# extras or conditional dependencies
marker
=
line
.
lstrip
(
'['
)
.
rstrip
(
']'
)
if
':'
not
in
marker
:
extra
,
marker
=
marker
,
None
else
:
extra
,
marker
=
marker
.
split
(
':'
)
if
extra
:
if
marker
:
marker
=
'{} and extra == "{}"'
.
format
(
marker
,
extra
)
else
:
marker
=
'extra == "{}"'
.
format
(
extra
)
if
marker
:
current_marker
=
marker
continue
if
current_marker
:
line
=
'{}; {}'
.
format
(
line
,
current_marker
)
requires_dist
.
append
(
line
)
if
requires_dist
:
return
requires_dist
def
_log
(
self
,
msg
,
level
=
'info'
):
def
_log
(
self
,
msg
,
level
=
'info'
):
getattr
(
logger
,
level
)(
'{}: {}'
.
format
(
self
.
_name
,
msg
))
getattr
(
logger
,
level
)(
'{}: {}'
.
format
(
self
.
_name
,
msg
))
poetry/utils/helpers.py
View file @
7a14aa59
...
@@ -3,6 +3,7 @@ import shutil
...
@@ -3,6 +3,7 @@ import shutil
import
tempfile
import
tempfile
from
contextlib
import
contextmanager
from
contextlib
import
contextmanager
from
typing
import
Union
_canonicalize_regex
=
re
.
compile
(
'[-_.]+'
)
_canonicalize_regex
=
re
.
compile
(
'[-_.]+'
)
...
@@ -28,3 +29,45 @@ def temporary_directory(*args, **kwargs):
...
@@ -28,3 +29,45 @@ def temporary_directory(*args, **kwargs):
yield
name
yield
name
shutil
.
rmtree
(
name
)
shutil
.
rmtree
(
name
)
def
parse_requires
(
requires
):
# type: (str) -> Union[list, None]
lines
=
requires
.
split
(
'
\n
'
)
requires_dist
=
[]
in_section
=
False
current_marker
=
None
for
line
in
lines
:
line
=
line
.
strip
()
if
not
line
:
if
in_section
:
in_section
=
False
continue
if
line
.
startswith
(
'['
):
# extras or conditional dependencies
marker
=
line
.
lstrip
(
'['
)
.
rstrip
(
']'
)
if
':'
not
in
marker
:
extra
,
marker
=
marker
,
None
else
:
extra
,
marker
=
marker
.
split
(
':'
)
if
extra
:
if
marker
:
marker
=
'{} and extra == "{}"'
.
format
(
marker
,
extra
)
else
:
marker
=
'extra == "{}"'
.
format
(
extra
)
if
marker
:
current_marker
=
marker
continue
if
current_marker
:
line
=
'{}; {}'
.
format
(
line
,
current_marker
)
requires_dist
.
append
(
line
)
if
requires_dist
:
return
requires_dist
tests/console/commands/test_add.py
View file @
7a14aa59
...
@@ -35,6 +35,11 @@ Writing lock file
...
@@ -35,6 +35,11 @@ Writing lock file
assert
len
(
installer
.
installs
)
==
1
assert
len
(
installer
.
installs
)
==
1
content
=
app
.
poetry
.
file
.
read
(
raw
=
True
)[
'tool'
][
'poetry'
]
assert
'cachy'
in
content
[
'dependencies'
]
assert
content
[
'dependencies'
][
'cachy'
]
==
'^0.2.0'
def
test_add_constraint
(
app
,
repo
,
installer
):
def
test_add_constraint
(
app
,
repo
,
installer
):
command
=
app
.
find
(
'add'
)
command
=
app
.
find
(
'add'
)
...
@@ -104,3 +109,71 @@ Writing lock file
...
@@ -104,3 +109,71 @@ Writing lock file
assert
len
(
installer
.
installs
)
==
2
assert
len
(
installer
.
installs
)
==
2
def
test_add_git_constraint
(
app
,
repo
,
installer
):
command
=
app
.
find
(
'add'
)
tester
=
CommandTester
(
command
)
repo
.
add_package
(
get_package
(
'pendulum'
,
'1.4.4'
))
tester
.
execute
([
(
'command'
,
command
.
get_name
()),
(
'name'
,
[
'demo'
]),
(
'--git'
,
'https://github.com/demo/demo.git'
)
])
expected
=
"""
\
Updating dependencies
Resolving dependencies..
Package operations: 2 installs, 0 updates, 0 removals
Writing lock file
- Installing pendulum (1.4.4)
- Installing demo (0.1.2 9cf87a2)
"""
assert
tester
.
get_display
()
==
expected
assert
len
(
installer
.
installs
)
==
2
content
=
app
.
poetry
.
file
.
read
(
raw
=
True
)[
'tool'
][
'poetry'
]
assert
'demo'
in
content
[
'dependencies'
]
assert
content
[
'dependencies'
][
'demo'
]
==
{
'git'
:
'https://github.com/demo/demo.git'
}
def
test_add_git_constraint_with_poetry
(
app
,
repo
,
installer
):
command
=
app
.
find
(
'add'
)
tester
=
CommandTester
(
command
)
repo
.
add_package
(
get_package
(
'pendulum'
,
'1.4.4'
))
tester
.
execute
([
(
'command'
,
command
.
get_name
()),
(
'name'
,
[
'demo'
]),
(
'--git'
,
'https://github.com/demo/pyproject-demo.git'
)
])
expected
=
"""
\
Updating dependencies
Resolving dependencies
Package operations: 2 installs, 0 updates, 0 removals
Writing lock file
- Installing pendulum (1.4.4)
- Installing demo (0.1.2 9cf87a2)
"""
assert
tester
.
get_display
()
==
expected
assert
len
(
installer
.
installs
)
==
2
tests/console/conftest.py
View file @
7a14aa59
import
pytest
import
pytest
import
shutil
try
:
import
urllib.parse
as
urlparse
except
ImportError
:
import
urlparse
from
poetry.config
import
Config
as
BaseConfig
from
poetry.config
import
Config
as
BaseConfig
from
poetry.console
import
Application
as
BaseApplication
from
poetry.console
import
Application
as
BaseApplication
...
@@ -17,6 +23,20 @@ def installer():
...
@@ -17,6 +23,20 @@ def installer():
return
NoopInstaller
()
return
NoopInstaller
()
def
mock_clone
(
self
,
source
,
dest
):
# Checking source to determine which folder we need to copy
parts
=
urlparse
.
urlparse
(
source
)
folder
=
(
Path
(
__file__
)
.
parent
.
parent
/
'fixtures'
/
'git'
/
parts
.
netloc
/
parts
.
path
.
lstrip
(
'/'
)
.
rstrip
(
'.git'
)
)
shutil
.
rmtree
(
dest
)
shutil
.
copytree
(
folder
,
dest
)
@pytest.fixture
(
autouse
=
True
)
@pytest.fixture
(
autouse
=
True
)
def
setup
(
mocker
,
installer
):
def
setup
(
mocker
,
installer
):
# Set Installer's installer
# Set Installer's installer
...
@@ -26,6 +46,12 @@ def setup(mocker, installer):
...
@@ -26,6 +46,12 @@ def setup(mocker, installer):
p
=
mocker
.
patch
(
'poetry.installation.installer.Installer._get_installed'
)
p
=
mocker
.
patch
(
'poetry.installation.installer.Installer._get_installed'
)
p
.
return_value
=
Repository
()
p
.
return_value
=
Repository
()
# Patch git module to not actually clone projects
mocker
.
patch
(
'poetry.vcs.git.Git.clone'
,
new
=
mock_clone
)
mocker
.
patch
(
'poetry.vcs.git.Git.checkout'
,
new
=
lambda
*
_
:
None
)
p
=
mocker
.
patch
(
'poetry.vcs.git.Git.rev_parse'
)
p
.
return_value
=
'9cf87a285a2d3fbb0b9fa621997b3acc3631ed24'
class
Application
(
BaseApplication
):
class
Application
(
BaseApplication
):
...
@@ -55,6 +81,9 @@ class Locker(BaseLocker):
...
@@ -55,6 +81,9 @@ class Locker(BaseLocker):
self
.
_lock_data
=
None
self
.
_lock_data
=
None
self
.
_content_hash
=
self
.
_get_content_hash
()
self
.
_content_hash
=
self
.
_get_content_hash
()
def
_write_lock_data
(
self
,
data
):
self
.
_lock_data
=
None
class
Poetry
(
BasePoetry
):
class
Poetry
(
BasePoetry
):
...
...
tests/fixtures/git/github.com/demo/demo/demo/__init__.py
0 → 100644
View file @
7a14aa59
tests/fixtures/git/github.com/demo/pyproject-demo/demo/__init__.py
0 → 100644
View file @
7a14aa59
tests/fixtures/git/github.com/demo/pyproject-demo/pyproject.toml
0 → 100644
View file @
7a14aa59
[tool.poetry]
name
=
"demo"
version
=
"0.1.2"
description
=
"Some description."
authors
=
[
"Sébastien Eustace <sebastien@eustace.io>"
]
license
=
"MIT"
# Requirements
[tool.poetry.dependencies]
python
=
"~2.7 || ^3.6"
pendulum
=
'^
1.4
'
[tool.poetry.dev-dependencies]
tests/repositories/test_pypi_repository.py
View file @
7a14aa59
...
@@ -64,52 +64,3 @@ def test_package_drops_malformed_dependencies():
...
@@ -64,52 +64,3 @@ def test_package_drops_malformed_dependencies():
dependency_names
=
[
d
.
name
for
d
in
package
.
requires
]
dependency_names
=
[
d
.
name
for
d
in
package
.
requires
]
assert
'setuptools'
not
in
dependency_names
assert
'setuptools'
not
in
dependency_names
def
test_parse_requires
():
requires
=
"""
\
jsonschema>=2.6.0.0,<3.0.0.0
lockfile>=0.12.0.0,<0.13.0.0
pip-tools>=1.11.0.0,<2.0.0.0
pkginfo>=1.4.0.0,<2.0.0.0
pyrsistent>=0.14.2.0,<0.15.0.0
toml>=0.9.0.0,<0.10.0.0
cleo>=0.6.0.0,<0.7.0.0
cachy>=0.1.1.0,<0.2.0.0
cachecontrol>=0.12.4.0,<0.13.0.0
requests>=2.18.0.0,<3.0.0.0
msgpack-python>=0.5.0.0,<0.6.0.0
pyparsing>=2.2.0.0,<3.0.0.0
requests-toolbelt>=0.8.0.0,<0.9.0.0
[:(python_version >= "2.7.0.0" and python_version < "2.8.0.0") or (python_version >= "3.4.0.0" and python_version < "3.5.0.0")]
typing>=3.6.0.0,<4.0.0.0
[:python_version >= "2.7.0.0" and python_version < "2.8.0.0"]
virtualenv>=15.2.0.0,<16.0.0.0
pathlib2>=2.3.0.0,<3.0.0.0
[:python_version >= "3.4.0.0" and python_version < "3.6.0.0"]
zipfile36>=0.1.0.0,<0.2.0.0
"""
result
=
MockRepository
()
.
_parse_requires
(
requires
)
expected
=
[
'jsonschema>=2.6.0.0,<3.0.0.0'
,
'lockfile>=0.12.0.0,<0.13.0.0'
,
'pip-tools>=1.11.0.0,<2.0.0.0'
,
'pkginfo>=1.4.0.0,<2.0.0.0'
,
'pyrsistent>=0.14.2.0,<0.15.0.0'
,
'toml>=0.9.0.0,<0.10.0.0'
,
'cleo>=0.6.0.0,<0.7.0.0'
,
'cachy>=0.1.1.0,<0.2.0.0'
,
'cachecontrol>=0.12.4.0,<0.13.0.0'
,
'requests>=2.18.0.0,<3.0.0.0'
,
'msgpack-python>=0.5.0.0,<0.6.0.0'
,
'pyparsing>=2.2.0.0,<3.0.0.0'
,
'requests-toolbelt>=0.8.0.0,<0.9.0.0'
,
'typing>=3.6.0.0,<4.0.0.0; (python_version >= "2.7.0.0" and python_version < "2.8.0.0") or (python_version >= "3.4.0.0" and python_version < "3.5.0.0")'
,
'virtualenv>=15.2.0.0,<16.0.0.0; python_version >= "2.7.0.0" and python_version < "2.8.0.0"'
,
'pathlib2>=2.3.0.0,<3.0.0.0; python_version >= "2.7.0.0" and python_version < "2.8.0.0"'
,
'zipfile36>=0.1.0.0,<0.2.0.0; python_version >= "3.4.0.0" and python_version < "3.6.0.0"'
]
assert
result
==
expected
tests/utils/test_helpers.py
0 → 100644
View file @
7a14aa59
from
poetry.utils.helpers
import
parse_requires
def
test_parse_requires
():
requires
=
"""
\
jsonschema>=2.6.0.0,<3.0.0.0
lockfile>=0.12.0.0,<0.13.0.0
pip-tools>=1.11.0.0,<2.0.0.0
pkginfo>=1.4.0.0,<2.0.0.0
pyrsistent>=0.14.2.0,<0.15.0.0
toml>=0.9.0.0,<0.10.0.0
cleo>=0.6.0.0,<0.7.0.0
cachy>=0.1.1.0,<0.2.0.0
cachecontrol>=0.12.4.0,<0.13.0.0
requests>=2.18.0.0,<3.0.0.0
msgpack-python>=0.5.0.0,<0.6.0.0
pyparsing>=2.2.0.0,<3.0.0.0
requests-toolbelt>=0.8.0.0,<0.9.0.0
[:(python_version >= "2.7.0.0" and python_version < "2.8.0.0") or (python_version >= "3.4.0.0" and python_version < "3.5.0.0")]
typing>=3.6.0.0,<4.0.0.0
[:python_version >= "2.7.0.0" and python_version < "2.8.0.0"]
virtualenv>=15.2.0.0,<16.0.0.0
pathlib2>=2.3.0.0,<3.0.0.0
[:python_version >= "3.4.0.0" and python_version < "3.6.0.0"]
zipfile36>=0.1.0.0,<0.2.0.0
"""
result
=
parse_requires
(
requires
)
expected
=
[
'jsonschema>=2.6.0.0,<3.0.0.0'
,
'lockfile>=0.12.0.0,<0.13.0.0'
,
'pip-tools>=1.11.0.0,<2.0.0.0'
,
'pkginfo>=1.4.0.0,<2.0.0.0'
,
'pyrsistent>=0.14.2.0,<0.15.0.0'
,
'toml>=0.9.0.0,<0.10.0.0'
,
'cleo>=0.6.0.0,<0.7.0.0'
,
'cachy>=0.1.1.0,<0.2.0.0'
,
'cachecontrol>=0.12.4.0,<0.13.0.0'
,
'requests>=2.18.0.0,<3.0.0.0'
,
'msgpack-python>=0.5.0.0,<0.6.0.0'
,
'pyparsing>=2.2.0.0,<3.0.0.0'
,
'requests-toolbelt>=0.8.0.0,<0.9.0.0'
,
'typing>=3.6.0.0,<4.0.0.0; (python_version >= "2.7.0.0" and python_version < "2.8.0.0") or (python_version >= "3.4.0.0" and python_version < "3.5.0.0")'
,
'virtualenv>=15.2.0.0,<16.0.0.0; python_version >= "2.7.0.0" and python_version < "2.8.0.0"'
,
'pathlib2>=2.3.0.0,<3.0.0.0; python_version >= "2.7.0.0" and python_version < "2.8.0.0"'
,
'zipfile36>=0.1.0.0,<0.2.0.0; python_version >= "3.4.0.0" and python_version < "3.6.0.0"'
]
assert
result
==
expected
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