4 ms·
I understand that the lack of standardization makes things difficult, but there are a lot of knowables. Further, there would be nothing wrong with returning so
by appplication 3y ago
I understand that the lack of standardization makes things difficult, but there are a lot of knowables.
Further, there would be nothing wrong with returning something like a NonconformingVersion object, where we can best guess at things like major, minor, patch. Parse what you think you know, and when you run into something that is nonconforming, then you fail over to return an object that has what you think you know and what you can’t sort out. E.g: in the above example, major, minor, patch versions are apparent. I would even say the vast majority of nonconforming versions have at least one or more of major/minor/patch readily parseable, the problem is we aren’t sure exactly how of handle the rest of it. And that’s fine.
This approach is super pythonic in the BTAFTP philosophy, where I will let users decide what to do with the object returned. “You asked me to parse this mess. I can’t fully do that, but here’s a best guess.” This is worlds better than just failing completely.
- woodruffw 3y agoI understand the frustration here (I maintain a project that was directly broken by strict version checking), but I also think that reducing the problem to "best guess at major, minor, patch" understates the problem here. For example, what do you do with version strings like these? a.b.c one.2.3 1.pre.2.3.4 2004d 1.0beta5prerelease 0.1-charmander 0.1-bulbasaur Those last two are actual legacy Python versions[1] -- how should we compare between those two for the purposes of dependency resolution? Whether or not it's preferable to attempt to parse unspecified inputs boils down to questions of predictability, reliability, and security and, IMO, security is an overriding factor when it comes to domains as critical as packaging. That being said: you can attempt to parse legacy versions using this package[2]. [1]: https://pypi.org/project/paramiko/#history https://pypi.org/project/paramiko/#history [2]: https://pypi.org/project/packaging-legacy/ https://pypi.org/project/packaging-legacy/
- appplication 3y agoWhen it comes to dependency resolution, that’s where it’s up to the application how to handle NonconformingVersion objects. For vanilla pip, it makes sense for it to treat these as errors. I actually think it makes sense for the PyPI itself to be the one to dictate how to handle version policy via some callable or handler. Many companies have internal versions and mirrors of OSS code, and allowing them to preferentially shim these in (and reasonably version) these for their employees. Let’s say you fork the requests library. So now your versioning is requests=2.31.0.corporate-31. Sure, it makes no sense externally, but as the internal PyPI authority, you can enforce this versioning. Then your users just point to your index and get what they need (even if they just ask for requests=2.31.0). In general, parsing and dependency resolution are two separate issues. The concerns of dependency resolution should not bleed into parsing. So it should be a valid use case to simply want to parse and compare two versions, and if it seems we have complete enough information, then make a truth estimation. E.g. in the above example, this should realistically not fail, but instead throw a NonconformingVersion warning: > assert parse(‘2.4.0.coudera2’) >= parse(‘2.4’) While we can’t parse the full string, we can make a really good, consistent guess at what the user is after. And if needed, parse could reasonably take an optional callable to support handling of nonconforming versions to convert them to conforming ones.