-
-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pyproject support #34
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you very much! I like most of the changes.
The Windows build failures are a bit of a blocker at the moment. And I suspect some of the unit tests are unnecessary, as they look like they were adapted from setup.py support routines that try to handle dynamic code instead of declarative values. Does poetry really support Python list comprehensions inside the pyproject.toml file?
setup.py
Outdated
@@ -68,6 +68,6 @@ | |||
'check-python-versions = check_python_versions.cli:main', | |||
], | |||
}, | |||
install_requires=['pyyaml'], | |||
install_requires=['pyyaml', 'pytomlpp'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather use tomli; python_version < 3.11
(except with the correct syntax for the python version check).
EDIT: unless pytomlpp is required for formatting-preserving write support or something like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are Appveyor builds failing to build pytomlpp? I see Windows wheels on PyPI, why is pip install not using them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not clear for me why pytomlpp is building on windows even if wheel exists, I've never used appveyor so I've no idea of what's happening.
I've no problems to change TOML package, even if I saw pytomlpp is faster, I use it just for this reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pyproject.toml
Outdated
@@ -0,0 +1,38 @@ | |||
[tool.poetry] | |||
name = "check-python-versions" | |||
version = "1.0.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't want to manually keep two sets of metadata in sync. All my current maintenance tooling supports reading/updating the version number in src/check_python_versions/__init__.py
.
I've never used Poetry and I'm not sure I want to switch to it at this time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you need it for the test suite, I suggest moving it inside tests/ somewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I put version metadata there to mimic the setup.py content, not to be used to build the package.
Into my packages, I usually have version number into __init__.py
and into pyproject.toml, kept in sync with commitizen
tool, FYI .
let me know if you prefer removing the version here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Version removed.
@@ -0,0 +1,197 @@ | |||
""" | |||
Support for pyproject.toml. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But AFAIU Poetry is not the only build tool that supports pyproject.toml? There are also things like flit, and I think even setuptools? (I'm not sure about setuptools -- I know there's a way to use declarative configuration for setuptools with an empty/minimal setup.py, but I don't know if it goes into setup.cfg, or if it can also go into pyproject.toml.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it can be made to work fine if you make the extract()
callback of the new Source check the value of [build-system] build-backend
and return None
if it doesn't match poetry.whatever
. Then multiple sources can declare that they support pyproject.toml and they should all cooperate nicely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But AFAIU Poetry is not the only build tool that supports pyproject.toml? There are also things like flit, and I think even setuptools? (I'm not sure about setuptools -- I know there's a way to use declarative configuration for setuptools with an empty/minimal setup.py, but I don't know if it goes into setup.cfg, or if it can also go into pyproject.toml.)
yes, recently many tools are starting support pyproject.toml instead of setup.py, but the content could differ (see setuptools as example, it does not start with [tool.poetry]
section but with [project]
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it can be made to work fine if you make the
extract()
callback of the new Source check the value of[build-system] build-backend
and returnNone
if it doesn't matchpoetry.whatever
. Then multiple sources can declare that they support pyproject.toml and they should all cooperate nicely.
surely it can be improved with other tools that are using pyproject.toml, I've to "study" your suggestion 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it can be made to work fine if you make the
extract()
callback of the new Source check the value of[build-system] build-backend
and returnNone
if it doesn't matchpoetry.whatever
. Then multiple sources can declare that they support pyproject.toml and they should all cooperate nicely.
Actually it's done, there are a plenty of utility methods to check which is the tools that handles the pyproject.toml file, with their tests.
some part are left unimplemented (even if the call is commented, e.g. this point and this and this) because I don't know the pyproject.toml structure used by setuptools and flit.
|
||
|
||
def _get_pyproject_toml_classifiers( | ||
filename: FileOrFilename = PYPROJECT_TOML |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit over-indented.
|
||
|
||
def _get_pyproject_toml_python_requires( | ||
filename: FileOrFilename = PYPROJECT_TOML |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likewise.
|
||
table['tool']['poetry']['classifiers'] = new_value | ||
|
||
return pytomlpp.dumps(table).split('\n') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah! Does this preserve formatting/comments? This would be a sufficiently good reason not to use tomli.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've done a test with my pyproject, and the dumped content is different than original one.
I've searched pytomlpp and seems there's no way to keep formatting/comments (and also the original toml style).
that dumps is used to create a big string with the content of entire file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched to tomlkit that keeps the original style and comments.
|
||
def _update_pyproject_toml_python_requires( | ||
filename: FileOrFilename, | ||
new_value: Union[str, List[str]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is overindented, and also I would like a trailing comma here.
[tool.poetry.dependencies] | ||
python = ', '.join([ | ||
'>=2.7', | ||
'!=3.0.*', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this actually valid TOML syntax, supported by Poetry?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's not a valid syntax
name='foo', | ||
classifiers=[ | ||
'Programming Language :: Python :: %s' % v | ||
for v in ['2.7', '3.7'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this actually valid syntax?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this actually valid syntax?
no, I'm going to remove the tests that uses this declaration way
assert ( | ||
"Did not understand python_requires formatting in python dependency" | ||
in capsys.readouterr().err | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to see a unit test that modifying the toml file leaves comments in place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added dedicated test
setuptools and flit are not implemented because struct not known
Removed the tests with dynamic code. Actually there is a minimum set of working tests and the build pass because of different toml library. |
added implementation&tests for setuptools and flit tools |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No time to do a full review today, but here are a few comments
pyproject.toml
Outdated
@@ -1,6 +1,5 @@ | |||
[tool.poetry] | |||
name = "check-python-versions" | |||
version = "1.0.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not the version that's a problem, it's that this project (check-python-versions) doesn't use Poetry and therefore it shouldn't have Poetry configuration in its top-level pyproject.toml.
Please move this file into tests/sources/data/
or a similarly named subdirectory if it's needed by the test suite.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's clear that this project doesn't use poetry to build it.
I've made the file to do a live check with check-python-versions.py , so it could be related to test_cli.py , I've to look at it.
@@ -38,11 +39,29 @@ | |||
PYPROJECT_TOML = 'pyproject.toml' | |||
|
|||
|
|||
def _load_toml(filename: FileOrFilename): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice if there was a type annotation for the return type. I suppose it's hard to be more specific than a generic Dict[str, Any]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems this row is outdated, also the file was renamed to "pyproject.py" because it handles all the tools that use pyproject.toml
with open_file(filename) as fp: | ||
table = load(fp) | ||
if isinstance(filename, TextIO): | ||
table = load(filename) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a check_python_version.utils.open_file()
context manager that could simplify this.
@@ -11,7 +11,8 @@ | |||
check-python-versions supports both. | |||
""" | |||
|
|||
import pytomlpp | |||
from tomlkit import dumps | |||
from tomlkit import parse, load |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
flake8 tells me tomlkit.parse is imported but unused.
def get_toml_content( | ||
filename: FileOrFilename = PYPROJECT_TOML | ||
) -> FileLines: | ||
"""Utility method to see if TOML library keeps style and comments.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's only used by the test suite, it should be moved into test_pyproject.py.
@@ -94,7 +116,7 @@ def get_python_requires( | |||
if python_requires is None: | |||
return None | |||
if not isinstance(python_requires, str): | |||
warn('The value passed to python is not a string') | |||
warn('The value passed to python dependency is not a string') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likewise.
@@ -111,7 +133,7 @@ def update_supported_python_versions( | |||
if classifiers is None: | |||
return None | |||
if not isinstance(classifiers, (list, tuple)): | |||
warn('The value passed to setup(classifiers=...) is not a list') | |||
warn('The value passed to classifiers is not a list') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likewise.
@@ -175,7 +195,7 @@ def _update_pyproject_toml_python_requires( | |||
return [] | |||
|
|||
table['tool']['poetry']['dependencies']['python'] = new_value | |||
return pytomlpp.dumps(table).split('\n') | |||
return dumps(table).split('\n') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to self: investigate what tomlkit.dumps() returns and whether this should be using .splitlines() instead.
Note to self: make sure the unit tests verify that modifying a .toml file doesn't add or remove any trailing newlines at the end of the FILE.
' \'Programming Language :: Python :: 3.6\',', | ||
' \'Programming Language :: Python :: 3.10\',', | ||
' ]', | ||
''] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would read better without the excessive indentation. Look at how other unit tests make assertions about the output. Here is one example:
check-python-versions/tests/sources/test_github.py
Lines 229 to 245 in 82c67ea
assert "".join(result) == textwrap.dedent("""\ | |
jobs: | |
build: | |
strategy: | |
matrix: | |
config: | |
# [Python version, tox env] | |
- ["3.8", "lint"] | |
- ["2.7", "py27"] | |
- ["3.8", "py38"] | |
- ["3.9", "py39"] | |
- ["pypy2", "pypy"] | |
- ["pypy3", "pypy3"] | |
- ["3.8", "coverage"] | |
steps: | |
- ... | |
""") |
# assert ( | ||
# 'The value passed to classifiers is not a list' | ||
# in capsys.readouterr().err | ||
# ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please do not leave commented-out tests in the file. Either remove entirely, or fix to make them pass, if it's a useful test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's an outdated state, latest commit have changed the file name to test_pyproject_poetry.py
Do tell me when you want me to take another look. I see there are still test failures, so I was thinking of waiting a bit. |
They’re failing because coverage is less than 100% . An improvement: is it possible to run both appveyor and GitHub action on all pushes? Because fixing issues from GitHub actions (that does not run always) after had fixed appveyor ones is a little bit demoralising for whom would contribute. |
Looks like GitHub requires me to approve the action run every time ("for first-time contributors"). I don't know if there's a way to avoid that. Oh how I hate cryptocurrency scammers that started abusing free CI and caused every CI provider to adopt these countermeasures. |
Is it something you can configure into settings?
Didn’t know about that. anyway, just reached again 100% of coverage. |
@mgedmin you can do it now, I've run all the checks locally and they all pass. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the delay, I was mostly offline for personal reasons.
I found a setting that relaxes the approval requirement for pull request to first-time GitHub users. Thank you for suggesting that there ought to be a setting for that somewhere!
There are some style concerns I have, but they're not critical. I might as well just merge this and fix things up more to my liking in git master.
Thank you!
The most important missing bit is describing the new feature in the changelog and crediting you.
if PYTHON not in \ | ||
table[TOOL][POETRY][DEPENDENCIES]: | ||
return None | ||
return cast(str, table[TOOL][POETRY][DEPENDENCIES][PYTHON]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Um, I checked Poetry's documentation and tools.poetry.dependencies.python
uses a different syntax (^3.8
instead of >= 3.8
), so this will not work right.
""")) | ||
fp.name = "pyproject.toml" | ||
result = update_python_requires(fp, v(['2.7', '3.2'])) | ||
assert result == ['[project]', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is wrong: the FileLines returned by update_... functions are supposed to include trailing newlines.
mypy 1.0 was released today (or yesterday? timezones, eh) and started complaining a lot about pyproject.py. While trying to fix those errors I ended up rewriting most of the code. I fixed some bugs that I noticed, but I'm afraid I might have introduced new ones, so some testing with actual Poetry-using projects would be appreciated. |
(None of the above would've happened if you hadn't opened this PR, for which I would once again like to thank you!) |
Thanks for this awesome package, I need it for my Poetry-managed projects ;) !
This is a possible support for pyproject.toml file managed with Poetry, many methods are reused from setup.py source.
Extra package to be installed is pytomlpp to manage toml files.
closes #21 .
EDIT: added management for setuptools and flit pyproject files