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
Rethinking __init__ of VersionInfo class? #303
Comments
I found another solution which doesn't need overloading. The previous signature of def __init__(
major: SupportsInt,
minor: SupportsInt = 0,
patch: SupportsInt = 0,
prerelease: Union[String, int] = None,
build: Union[String, int] = None,
) This changed now to this: def __init__(self, *args: Tuple, **kwargs: Dict):
# print(f">> args={args} {len(args)}, kwargs={kwargs} {len(kwargs)}")
verlist = [None, None, None, None, None]
if not args and not kwargs:
raise ValueError("No argument given.")
if len(args) > 5:
raise ValueError("You cannot pass more than 5 arguments to Version")
if args and "." in str(args[0]):
# we have a version string as first argument
cls = self.__class__
v = cls._parse(args[0])
self._major = int(v["major"])
self._minor = int(v["minor"])
self._patch = int(v["patch"])
self._prerelease = v["prerelease"]
self._build = v["build"]
return
for index, item in enumerate(args):
verlist[index] = args[index]
# Build a dictionary of the arguments except prerelease and build
try:
version_parts = {
# Prefer kwargs over args
"major": int(kwargs.pop("major", 0) or verlist[0] or 0),
"minor": int(kwargs.pop("minor", 0) or verlist[1] or 0),
"patch": int(kwargs.pop("patch", 0) or verlist[2] or 0)
}
except ValueError:
raise ValueError("Expected integer or integer string for major, "
"minor, or patch")
prerelease = kwargs.pop("prerelease", None)
build = kwargs.pop("build", None)
# Check, if kwargs contains other unknown keys:
if kwargs:
raise ValueError("Unknown key passed in {n}: {k}".format(
n=self.__class__.__name__,
k=", ".join([f"{k}={v}"
for k, v in kwargs.items()])))
for name, value in version_parts.items():
if value < 0:
raise ValueError(
"{!r} is negative. A version can only be positive.".format(name)
)
self._major = version_parts["major"]
self._minor = version_parts["minor"]
self._patch = version_parts["patch"]
self._prerelease = None if prerelease is None else str(prerelease)
self._build = None if build is None else str(build) This allows some interesting variations: >>> import semver
>>> semver.Version(1, 2, 3)
Version(major=1, minor=2, patch=3, prerelease=None, build=None)
>>> semver.Version("1.2.3")
Version(major=1, minor=2, patch=3, prerelease=None, build=None)
>>> semver.Version(1, minor="2", patch=3)
Version(major=1, minor=2, patch=3, prerelease=None, build=None) You can also use positional arguments and keyword arguments to "overwrite" one or the other: >>> semver.Version(1, major="2", patch=3)
Version(major=2, minor=0, patch=3, prerelease=None, build=None) Probably the code can be improved, but it's just a proof-of-concept to show this idea. Additionally, it avoids the complicated overload code. What do you think? |
* Allow different variants to call Version * Adapt the documentation and README * Adapt and amend tests * Add changelog entries TODO: Fix typing errors
If the above PR got accepted, the |
* Allow different variants to call Version * Adapt the documentation and README * Adapt and amend tests * Add changelog entries TODO: Fix typing errors
* Allow different variants to call Version * Adapt the documentation and README * Adapt and amend tests * Add changelog entries TODO: Fix typing errors
* Allow different variants to call Version * Adapt the documentation and README * Adapt and amend tests * Add changelog entries * Introduce a (private) _ensure_str class method
* Allow different variants to call Version * Adapt the documentation and README * Adapt and amend tests * Add changelog entries * Introduce a (private) _ensure_str class method
* Allow different variants to call Version * Adapt the documentation and README * Adapt and amend tests * Add changelog entries * Introduce a (private) _ensure_str class method
* Allow different variants to call Version * Adapt the documentation and README * Adapt and amend tests * Add changelog entries * Introduce a (private) _ensure_str class method
* Allow different variants to call Version * Adapt the documentation and README * Adapt and amend tests * Add changelog entries * Introduce a (private) _ensure_str class method
* Allow different variants to call Version * Adapt the documentation and README * Adapt and amend tests * Add changelog entries * Introduce a (private) _ensure_str class method
* Allow different variants to call Version * Adapt the documentation and README * Adapt and amend tests * Add changelog entries * Add function "remove_noqa" in conf.py to remove any "# noqa" lines for flake8 issues like overly long lines * Introduce a (private) _ensure_str class method
* Allow different variants to call Version * Adapt the documentation and README * Adapt and amend tests * Add changelog entries * Add function "remove_noqa" in conf.py to remove any "# noqa" lines for flake8 issues like overly long lines * Introduce a (private) _ensure_str class method
* Allow different variants to call Version * Adapt the documentation and README * Adapt and amend tests * Add changelog entries * Add function "remove_noqa" in conf.py to remove any "# noqa" lines for flake8 issues like overly long lines * Introduce a (private) _ensure_str class method
* Allow different variants to call Version * Adapt the documentation and README * Adapt and amend tests * Add changelog entries * Add function "remove_noqa" in conf.py to remove any "# noqa" lines for flake8 issues like overly long lines * Introduce a (private) _ensure_str class method
* Allow different variants to call Version * Adapt the documentation and README * Adapt and amend tests * Add changelog entries * Add function "remove_noqa" in conf.py to remove any "# noqa" lines for flake8 issues like overly long lines * Introduce a (private) _ensure_str class method
* Allow different variants to call Version * Adapt the documentation and README * Adapt and amend tests * Add changelog entries * Add function "remove_noqa" in conf.py to remove any "# noqa" lines for flake8 issues like overly long lines * Introduce a (private) _ensure_str class method
* Allow different variants to call Version * Adapt the documentation and README * Adapt and amend tests * Add changelog entries * Add function "remove_noqa" in conf.py to remove any "# noqa" lines for flake8 issues like overly long lines * Introduce a (private) _ensure_str class method
Situation
Originally posted by @tomschr in #258 (comment)
From the above discussion, I thought it would be worth to decouple the
compare
discussion from the initializer discussion. Both are somewhat related, but can live independently.With a more "advanced" initializer/constructor we get the following benefits:
Version.parse(...)
.With such an (overloaded?) initializer, we could cover the following use cases:
Discussions and Possible Solutions
To implement a somewhat more advanced constructor/initializer, we have these options:
isinstance
and if...else constructstyping.overload
function (suggested by @tlaferriere)functools.singledispatch
However, all three comes at a cost or an issue:
@overload
decorator is purely for type hints, you can only specify one function body and it has to distinguish the different types using isinstance.` (Originally posted by @tlaferriere in Consider keepingcompare
module level function #258 (comment))__init__
method. Thesingledispatch
works only for functions(!), not methods. For methods we would needfunctools.singledispatchmethod
which is only available from Python >= 3.8.Another idea goes into a completely different direction. Maybe we shouldn't change the
Version
class much, but offer a much shorter variant:semver.v
.Which means, we could just use
semver.v
as a much shorter variant:One drawback could be that
v
is quite short. Maybe too short? Especially if you import it withfrom semver import v
it could be easily overwritten by other, local variables.That could be a bit avoided to use capital
V
orver
. Or we use the much longer namesemver.version
.Thoughts? Any other ideas? Would that be worth the effort?
@tlaferriere @gsakkis @ppkt
The text was updated successfully, but these errors were encountered: