Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
后端镜像
Pdm
提交
5b106c48
P
Pdm
项目概览
后端镜像
/
Pdm
通知
0
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
P
Pdm
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
5b106c48
编写于
3月 17, 2020
作者:
F
frostming
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
support poetry converting
上级
18579641
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
278 addition
and
20 deletion
+278
-20
pdm/formats/base.py
pdm/formats/base.py
+60
-0
pdm/formats/poetry.py
pdm/formats/poetry.py
+115
-0
pdm/formats/requirements.py
pdm/formats/requirements.py
+54
-10
pdm/models/requirements.py
pdm/models/requirements.py
+9
-2
tests/models/test_requirements.py
tests/models/test_requirements.py
+2
-7
tests/test_formats.py
tests/test_formats.py
+38
-1
未找到文件。
pdm/formats/base.py
0 → 100644
浏览文件 @
5b106c48
import
abc
import
collections
def
convert_from
(
field
=
None
,
name
=
None
):
def
wrapper
(
func
):
func
.
_convert_from
=
field
func
.
_convert_to
=
name
return
func
return
wrapper
class
_MetaConverterMeta
(
abc
.
ABCMeta
):
def
__init__
(
cls
,
name
,
bases
,
ns
):
super
().
__init__
(
name
,
bases
,
ns
)
cls
.
_converters
=
{}
_default
=
object
()
for
key
,
value
in
ns
.
items
():
if
getattr
(
value
,
"_convert_from"
,
_default
)
is
not
_default
:
name
=
value
.
_convert_to
or
key
cls
.
_converters
[
name
]
=
value
class
MetaConverter
(
collections
.
abc
.
Mapping
,
metaclass
=
_MetaConverterMeta
):
"""Convert a metadata dictionary to PDM's format"""
def
__init__
(
self
,
source
):
self
.
_data
=
{}
self
.
_convert
(
dict
(
source
))
def
__getitem__
(
self
,
k
):
return
self
.
_data
[
k
]
def
__len__
(
self
):
return
len
(
self
.
_data
)
def
__iter__
(
self
):
return
iter
(
self
.
_data
)
def
_convert
(
self
,
source
):
for
key
,
func
in
self
.
_converters
.
items
():
if
func
.
_convert_from
and
func
.
_convert_from
not
in
source
:
continue
if
func
.
_convert_from
is
None
:
value
=
source
else
:
value
=
source
[
func
.
_convert_from
]
self
.
_data
[
key
]
=
func
(
self
,
value
)
# Delete all used fields
for
key
,
func
in
self
.
_converters
.
items
():
if
func
.
_convert_from
is
None
:
continue
try
:
del
source
[
func
.
_convert_from
]
except
KeyError
:
pass
# Add remaining items to the data
self
.
_data
.
update
(
source
)
pdm/formats/poetry.py
浏览文件 @
5b106c48
import
functools
import
operator
import
re
import
tomlkit
from
pdm.formats.base
import
MetaConverter
,
convert_from
from
pdm.models.markers
import
Marker
from
pdm.models.specifiers
import
PySpecSet
def
check_fingerprint
(
filename
):
with
open
(
filename
,
encoding
=
"utf-8"
)
as
fp
:
data
=
tomlkit
.
parse
(
fp
.
read
())
return
"tool"
in
data
and
"poetry"
in
data
[
"tool"
]
VERSION_RE
=
re
.
compile
(
r
"(\S+?)\s*(\d.*?)\s*(?=,|$)"
)
def
_convert_specifier
(
version
):
parts
=
[]
for
op
,
version
in
VERSION_RE
.
findall
(
version
):
if
op
==
"~"
:
op
+=
"="
elif
op
==
"^"
:
major
,
*
vparts
=
version
.
split
(
"."
)
next_major
=
"."
.
join
([
str
(
int
(
major
)
+
1
)]
+
[
"0"
]
*
len
(
vparts
))
parts
.
append
(
f
">=
{
version
}
,<
{
next_major
}
"
)
continue
parts
.
append
(
f
"
{
op
}{
version
}
"
)
return
","
.
join
(
parts
)
def
_convert_python
(
python
):
if
not
python
:
return
""
parts
=
[
PySpecSet
(
_convert_specifier
(
s
))
for
s
in
python
.
split
(
"||"
)]
return
functools
.
reduce
(
operator
.
or_
,
parts
)
def
_convert_req
(
req_dict
):
if
not
getattr
(
req_dict
,
"items"
,
None
):
return
_convert_specifier
(
req_dict
)
req_dict
=
dict
(
req_dict
)
if
"version"
in
req_dict
:
req_dict
[
"version"
]
=
_convert_specifier
(
req_dict
[
"version"
])
markers
=
[]
if
"markers"
in
req_dict
:
markers
.
append
(
Marker
(
req_dict
.
pop
(
"markers"
)))
if
"python"
in
req_dict
:
markers
.
append
(
Marker
(
_convert_python
(
req_dict
.
pop
(
"python"
)).
as_marker_string
())
)
if
markers
:
req_dict
[
"marker"
]
=
str
(
functools
.
reduce
(
operator
.
and_
,
markers
))
if
"rev"
in
req_dict
or
"branch"
in
req_dict
or
"tag"
in
req_dict
:
req_dict
[
"ref"
]
=
req_dict
.
pop
(
"rev"
,
req_dict
.
pop
(
"tag"
,
req_dict
.
pop
(
"branch"
,
None
))
)
return
req_dict
class
PoetryMetaConverter
(
MetaConverter
):
@
convert_from
(
"authors"
)
def
author
(
self
,
value
):
return
value
[
0
]
@
convert_from
(
"maintainers"
)
def
maintainer
(
self
,
value
):
return
value
[
0
]
@
convert_from
()
def
python_requires
(
self
,
source
):
python
=
source
.
get
(
"dependencies"
,
{}).
pop
(
"python"
,
None
)
return
str
(
_convert_python
(
python
))
@
convert_from
()
def
project_urls
(
self
,
source
):
rv
=
source
.
pop
(
"urls"
,
{})
if
"repository"
in
source
:
rv
[
"Repository"
]
=
source
.
pop
(
"repository"
)
if
"documentation"
in
source
:
rv
[
"Documentation"
]
=
source
.
pop
(
"documentation"
)
return
rv
@
convert_from
(
"scripts"
)
def
cli
(
self
,
value
):
return
value
@
convert_from
(
"plugins"
)
def
entry_points
(
self
,
value
):
return
value
@
convert_from
()
def
dependencies
(
self
,
source
):
rv
=
{}
value
,
extras
=
dict
(
source
[
"dependencies"
]),
source
.
pop
(
"extras"
,
{})
for
key
,
req_dict
in
value
.
items
():
optional
=
getattr
(
req_dict
,
"items"
,
None
)
and
req_dict
.
pop
(
"optional"
,
False
)
req_dict
=
_convert_req
(
req_dict
)
if
optional
:
extra
=
next
((
k
for
k
,
v
in
extras
.
items
()
if
key
in
v
),
None
)
if
extra
:
self
.
_data
.
setdefault
(
f
"
{
extra
}
-dependencies"
,
{})[
key
]
=
req_dict
else
:
rv
[
key
]
=
req_dict
if
extras
:
self
.
_data
[
"extras"
]
=
list
(
extras
)
del
source
[
"dependencies"
]
return
rv
@
convert_from
(
"dev-dependencies"
,
name
=
"dev-dependencies"
)
def
dev_dependencies
(
self
,
value
):
return
{
key
:
_convert_req
(
req
)
for
key
,
req
in
value
.
items
()}
def
convert
(
filename
):
with
open
(
filename
,
encoding
=
"utf-8"
)
as
fp
:
return
dict
(
PoetryMetaConverter
(
tomlkit
.
parse
(
fp
.
read
())[
"tool"
][
"poetry"
]))
pdm/formats/requirements.py
浏览文件 @
5b106c48
import
hashlib
from
pip._internal.req.req_file
import
parse_requirements
from
pdm.models.requirements
import
Requirement
from
pdm.models.markers
import
Marker
from
pdm.models.requirements
import
parse_requirement
from
pdm.utils
import
get_finder
def
_requirement_to_str_lowercase_name
(
requirement
):
"""Formats a packaging.requirements.Requirement with a lowercase name."""
parts
=
[
requirement
.
name
.
lower
()]
if
requirement
.
extras
:
parts
.
append
(
"[{0}]"
.
format
(
","
.
join
(
sorted
(
requirement
.
extras
))))
if
requirement
.
specifier
:
parts
.
append
(
str
(
requirement
.
specifier
))
if
requirement
.
url
:
parts
.
append
(
"@ {0}"
.
format
(
requirement
.
url
))
if
requirement
.
marker
:
parts
.
append
(
"; {0}"
.
format
(
requirement
.
marker
))
return
""
.
join
(
parts
)
def
requirement_from_ireq
(
ireq
):
"""Formats an `InstallRequirement` instance as a
`pdm.models.requirement.Requirement`.
Generic formatter for pretty printing InstallRequirements to the terminal
in a less verbose way than using its `__str__` method.
:param :class:`InstallRequirement` ireq: A pip **InstallRequirement** instance.
:return: A formatted string for prettyprinting
:rtype: str
"""
if
ireq
.
editable
:
line
=
"{}"
.
format
(
ireq
.
link
)
else
:
line
=
_requirement_to_str_lowercase_name
(
ireq
.
req
)
if
str
(
ireq
.
req
.
marker
)
!=
str
(
ireq
.
markers
):
if
not
ireq
.
req
.
marker
:
line
=
"{}; {}"
.
format
(
line
,
ireq
.
markers
)
else
:
name
,
markers
=
line
.
split
(
";"
,
1
)
markers
=
Marker
(
markers
)
&
ireq
.
markers
line
=
"{}; {}"
.
format
(
name
,
markers
)
return
parse_requirement
(
line
,
ireq
.
editable
)
def
parse_requirement_file
(
filename
):
finder
=
get_finder
([])
ireqs
=
list
(
parse_requirements
(
filename
,
finder
.
session
,
finder
))
...
...
@@ -26,19 +74,15 @@ def check_fingerprint(filename):
def
convert_url_to_source
(
url
,
name
=
None
):
if
not
name
:
name
=
hashlib
.
sha1
(
url
.
encode
(
"utf-8"
)).
hexdigest
[:
6
]
name
=
hashlib
.
sha1
(
url
.
encode
(
"utf-8"
)).
hexdigest
()
[:
6
]
return
{
"name"
:
name
,
"url"
:
url
,
"verify_ssl"
:
url
.
startswith
(
"https://"
)}
def
convert
(
filename
):
ireqs
,
finder
=
parse_requirement_file
(
filename
)
reqs
=
[]
for
ireq
in
ireqs
:
req
=
ireq
.
req
req
.
marker
=
ireq
.
markers
reqs
.
append
(
Requirement
.
from_pkg_requirement
(
req
))
data
=
{
"dependencies"
:
dict
(
req
.
to_req_dict
()
for
req
in
reqs
)}
ireqs
,
finder
=
parse_requirement_file
(
str
(
filename
))
reqs
=
[
requirement_from_ireq
(
ireq
)
for
ireq
in
ireqs
]
data
=
{
"dependencies"
:
dict
(
req
.
as_req_dict
()
for
req
in
reqs
)}
if
finder
.
index_urls
:
sources
=
[
convert_url_to_source
(
finder
.
index_urls
[
0
],
"pypi"
)]
sources
.
extend
(
convert_url_to_source
(
url
)
for
url
in
finder
.
index_urls
[
1
:])
...
...
pdm/models/requirements.py
浏览文件 @
5b106c48
...
...
@@ -65,6 +65,7 @@ class Requirement:
"project_name"
,
"url"
,
"path"
,
"ref"
,
"index"
,
"version"
,
"allow_prereleases"
,
...
...
@@ -145,7 +146,7 @@ class Requirement:
r
[
"version"
]
=
"*"
if
len
(
r
)
==
1
and
next
(
iter
(
r
),
None
)
==
"version"
:
r
=
r
[
"version"
]
for
attr
in
[
"index"
,
"allow_prereleases"
]:
for
attr
in
[
"index"
,
"allow_prereleases"
,
"ref"
]:
if
getattr
(
self
,
attr
)
is
not
None
:
r
[
attr
]
=
getattr
(
self
,
attr
)
return
self
.
project_name
,
r
...
...
@@ -344,7 +345,13 @@ class VcsRequirement(FileRequirement):
self
.
_parse_name_from_url
()
if
not
self
.
name
:
raise
RequirementError
(
"VCS requirement must provide a 'egg=' fragment."
)
self
.
repo
=
url_without_fragments
(
self
.
url
)
repo
=
url_without_fragments
(
self
.
url
)
ref
=
None
parsed
=
urlparse
.
urlparse
(
repo
)
if
"@"
in
parsed
.
path
:
path
,
ref
=
parsed
.
path
.
split
(
"@"
,
1
)
repo
=
urlparse
.
urlunparse
(
parsed
.
_replace
(
path
=
path
))
self
.
repo
,
self
.
ref
=
repo
,
ref
@
staticmethod
def
_build_url_from_req_dict
(
name
:
str
,
url
:
str
,
req_dict
:
RequirementDict
)
->
str
:
...
...
tests/models/test_requirements.py
浏览文件 @
5b106c48
...
...
@@ -33,13 +33,8 @@ REQUIREMENTS = [
None
,
),
(
"git+http://git.example.com/MyProject#egg=MyProject"
,
(
"MyProject"
,
{
"git"
:
"http://git.example.com/MyProject"
}),
None
,
),
(
"git+http://git.example.com/MyProject#egg=MyProject"
,
(
"MyProject"
,
{
"git"
:
"http://git.example.com/MyProject"
}),
"git+http://git.example.com/MyProject.git@master#egg=MyProject"
,
(
"MyProject"
,
{
"git"
:
"http://git.example.com/MyProject.git"
,
"ref"
:
"master"
}),
None
,
),
(
...
...
tests/test_formats.py
浏览文件 @
5b106c48
from
pdm.formats
import
pipfile
,
requirements
from
pdm.formats
import
pipfile
,
poetry
,
requirements
from
tests
import
FIXTURES
...
...
@@ -17,3 +17,40 @@ def test_convert_pipfile():
assert
result
[
"dependencies"
][
"pywinusb"
][
"marker"
]
==
'sys_platform == "win32"'
assert
result
[
"source"
][
0
][
"url"
]
==
"https://pypi.python.org/simple"
def
test_convert_requirements_file
():
golden_file
=
FIXTURES
/
"requirements.txt"
assert
requirements
.
check_fingerprint
(
golden_file
)
result
=
requirements
.
convert
(
golden_file
)
assert
len
(
result
[
"source"
])
==
2
assert
result
[
"dependencies"
][
"webassets"
]
==
"==2.0"
assert
result
[
"dependencies"
][
"whoosh"
][
"marker"
]
==
'sys_platform == "win32"'
assert
result
[
"dependencies"
][
"pip"
][
"editable"
]
assert
result
[
"dependencies"
][
"pip"
][
"git"
]
==
"https://github.com/pypa/pip.git"
def
test_convert_poetry
():
golden_file
=
FIXTURES
/
"pyproject-poetry.toml"
assert
poetry
.
check_fingerprint
(
golden_file
)
result
=
poetry
.
convert
(
golden_file
)
assert
result
[
"author"
]
==
"Sébastien Eustace <sebastien@eustace.io>"
assert
result
[
"name"
]
==
"poetry"
assert
result
[
"version"
]
==
"1.0.0"
assert
"Repository"
in
result
[
"project_urls"
]
assert
result
[
"python_requires"
]
==
">=2.7,<4.0,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
assert
result
[
"dependencies"
][
"cleo"
][
"marker"
]
==
'python_version ~= "2.7"'
assert
result
[
"dependencies"
][
"cachecontrol"
][
"marker"
]
==
(
'python_version >= "3.4" and python_version < "4.0"'
)
assert
"psycopg2"
not
in
result
[
"dependencies"
]
assert
"psycopg2"
in
result
[
"pgsql-dependencies"
]
assert
sorted
(
result
[
"extras"
])
==
[
"mysql"
,
"pgsql"
]
assert
len
(
result
[
"dev-dependencies"
])
==
2
assert
result
[
"cli"
]
==
{
"poetry"
:
"poetry.console:run"
}
assert
result
[
"entry_points"
][
"blogtool.parsers"
]
==
{
".rst"
:
"some_module:SomeClass"
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录