-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
[RFC] [PoC] [WIP] Implement dynamic (SCM) version loader #672
[RFC] [PoC] [WIP] Implement dynamic (SCM) version loader #672
Conversation
cc @kevinkjt2000 @madig @escaped @RonnyPfannschmidt @glehmann @seglberg @whoiswes @psafont ^^ |
@@ -1,6 +1,6 @@ | |||
[tool.poetry] | |||
name = "poetry" | |||
version = "0.12.8" | |||
version = "attr: setuptools_scm.get_version" |
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 there be a way to enable setuptools_scm to provide the version more directly?
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.
Currently, version arg is read unconditionally + I didn't notice any hooks in 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.
Of course calling setuptools_scm could be hardcoded in python source, but that's a question about flexibility
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.
What is meant by this question? It looks to me like the setuptools_scm library is directly responsible for providing the version in this example.
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.
@kevinkjt2000 This example only shows that users can refer to any callable they want. setuptools_scm has been chosen as a common example but any function will work. That's flexibility.
OTOH @RonnyPfannschmidt seems to want smth like use_version scm = True
to work without having to refer to the function explicitly.
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.
@kevinkjt2000 for example in the setuptools integration we trigger the integration and the file finder via the setuptools plugin system
for the poetry integration something similar could be thinkable
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'm a mediocre dev at best and can't intelligently comment, but I would prefer to pass a callable to the config versus offering any integrations. That seems to offer the most flexibility and least complexity in my book.
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 may have a lack of understanding here, but to trigger the integration and the file finder via the setuptools plugin system
is that not what the get_version function does?
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.
@kevinkjt2000 setuptools has entry_point-based hook system. Other packages can specify their entry points with those name to "register" their hook implementations and setuptools then automatically knows it can use them. Example: https://github.com/pypa/setuptools_scm/blob/master/setup.py#L69
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 like it. Accepting a callable function should allow users to do what they want 😄
Still needs tests, of course.
@@ -8,6 +8,23 @@ | |||
|
|||
class ProjectPackage(Package): | |||
def __init__(self, name, version, pretty_version=None): | |||
if isinstance(version, str) and version.startswith('attr:'): | |||
version_pkg, _, version_attr = ( | |||
version[6:].strip().rpartition('.') |
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.
Should "attr:function_name"
be allowed, without a space after the colon?
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 not? It's harmless and it's how it works in setuptools.
@@ -8,6 +8,23 @@ | |||
|
|||
class ProjectPackage(Package): | |||
def __init__(self, name, version, pretty_version=None): | |||
if isinstance(version, str) and version.startswith('attr:'): |
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.
The init code would look a little cleaner by moving this code into a new function.
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.
Sure, it's just a PoC.
if version is None: | ||
raise RuntimeError( | ||
'The version getter callable must return a string ' | ||
'or a Version instance' |
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.
The message here does not match what the conditional is checking.
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.
Yes, this should be fixed.
__import__(version_pkg, fromlist=(version_pkg, )), | ||
version_attr, | ||
) | ||
version = version_getter() |
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.
People may want to import a variable instead of a function. The setuptools_scm documentation shows an example in the programmatic usage section https://github.com/pypa/setuptools_scm#programmatic-usage
Checking if the attribute is callable would allow for this. https://docs.python.org/3/library/functions.html#callable
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.
That's different. Also the callable may as well return that constant.
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.
Constant declaration is already supported by setting it in pyproject.toml
. There should be only one way to do it.
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.
The variable is dynamically determined; I do not think this is already supported by pyproject.toml
. I think adding a small function wrapper that returns a variable is an acceptable thing. But a small if check for whether or not it is callable
would support both. Tiny quality of life thing that saves writing a tiny function for people who copy/paste from setuptools_scm docs.
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.
You're suggesting to put import-time evaluation in place, which is exactly what doomed setuptools. And now because of backwards compat they cannot fix this.
That's why I'm -1
here.
If one wants to access the installed version info from their code we could provide them a helper function to do so but let's not do import-time version detection. After all, app needs the version retrieved from pkg_resources in installed mode, it shouldn't invoke version-guessing flows in this case.
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.
What is the difference in what is accomplished between a variable that holds the version, and a function that evaluates to a variable that holds the version? I was suggesting this:
if callable(version_getter):
version = version_getter()
else:
version = version_getter
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.
The difference is that it encourages users to have
version = blah_blah()
called on during the module import, which is bad.
@warner, the python-versioneer author, might also be interested in this discussion :) |
@RonnyPfannschmidt would it make sense to reuse setuptools' entry points to retrieve the version getter from integrations? Do you think this'll make existing ones "just work"? |
@webknjaz it would be highly problematic to try and replicate any of the hook systems of setuptools with sanity - it would never amount to more than a really ugly hack - i strongly advise towards having own integration points that match the design of poetry |
Good point. Let's see what @sdispater thinks... |
It looks like poetry has a similar plugin system: https://poetry.eustace.io/docs/pyproject/#plugins |
@sdispater what do you think? |
@webknjaz as far as i understood, what you linked is how poetry declares entry-points - thats not yet an actual extension system, just metadata that pkg_resources or the entrypoints package may consume |
@RonnyPfannschmidt it seems you're right, but it's the closest I've managed to google |
I'd also change the behaviour of Consider showing a message on how to tag the commit with the scm when a function is detected on the |
Good catch! |
I won't make any changes to PR until I hear from @sdispater to avoid having to remake everything multiple times :) |
Thanks for taking the time to make this PR. I will hold off this feature for now though. This is until Poetry can provide a proper plugin system so that people can opt in this behavior by installing the correct plugin. I can't give you an ETA since implementing a plugin system might take some time. |
Sad to hear. Can we help with this somehow? This is one of the features which is holding me from switching to Poetry completely. |
Is there any architectural vision on plugins at all, currently? Ref #658 |
I thought I'd raise an actual issue about the plugin system: #693. |
@webjnjaz thanks for putting the time in this. This is also holding me back from using poetry. |
@zeebonk it looks like it's in progress by @sdispater already: https://github.com/sdispater/poetry/projects/1. And it's been added to https://github.com/sdispater/poetry/milestone/1 |
@webknjaz that's great news! Subscribed :) |
Closing this for now. I'll ping you when the plugin system is implemented. |
Okay, thanks |
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
This PR provides a sketch of how the developed dist version hardcoding could be avoided.
It is not a final implementation, but rather a request for comments.
I'd like to identify:
Currently it's a
setup.cfg
-like "attr:
" tag referencing to an importable callable, which gives users a lot of flexibility while preserving poetry's built-in validation mechanismsPull Request Check List