-
Notifications
You must be signed in to change notification settings - Fork 443
Refactor datetime #518
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
base: master
Are you sure you want to change the base?
Refactor datetime #518
Conversation
Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
This will be a recurring theme in the commits that follow. The check can be explained as follows: the original check is: if ... time_util.after(timestamp): raise ... which can be thought of as: is_vaild = not time_util.after(datetime) means that this is valid if the current datetime is not after the given datetime. IOW, this is valid if the current datetime is before the given datetime or, swapping the comparison elements, this is valid if the given datetime is after the current datetime. The equivalent new check is: is_valid = saml2.datetime.compare.after_now(date_time) means that this is valid if the given datetime is after the current datetime. Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
if .get_notBefore() returned None (which is allowed to do so) then the code would break. If notBefore is None then the certificate is always valid for that check. Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
Codecov Report
@@ Coverage Diff @@
## master #518 +/- ##
==========================================
+ Coverage 65.17% 65.26% +0.08%
==========================================
Files 100 109 +9
Lines 25691 25741 +50
==========================================
+ Hits 16745 16799 +54
+ Misses 8946 8942 -4
Continue to review full report at Codecov.
|
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.
Overall a great addition to pysaml2 that will simplify all time manipulation and validation.
- int: a number representing a POSIX timestamp | ||
- float: a number representing a POSIX timestamp | ||
|
||
The object returned is of type: datetime.datetime |
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 your current comments about arguments and types reads better but would you (also?) consider type hints for IDEs?
Something like:
"""Return a datetime object in UTC timezone from the given data.
*snip*
:param data: Representation of time
:type data: datetime.datetime|str|int|float
:return: Datetime object
:rtype: datetime.datetime
"""
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 Python3 type hints, as they are inline and they refer to actual types - in contrast to types written as strings (wether in docstrings or comments).
I was thinking about experimenting with function annotations which should work with Python2 and still allow us to signal actual types rather than strings. It does have the drawback that we must type the parameter names, but I think it is still better.
In the meantime, I can do the conversion to the format that the rest of the code follows.
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 also prefer the Python 3 annotations and I haven't seen the function annotations that you link to. I wasn't thrilled about the syntax of those either but if you find it workable in your experiment I would be for them.
"""Return if dt is within period amount before and after the current datetime. | ||
|
||
Return whether the datetime object `dt` is equal or later than the current | ||
UTC datetime object decreased by the given perid object and earlier than |
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.
perid -> period
return dt1 <= dt < dt2 | ||
|
||
|
||
def within_now(period, dt): |
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 assume that "within now" would have now as the lower boundary for the calculation. This is why I shouldn't assume :). Maybe within_period would be a better name? But that also raises the question if the period is a full period behind now to a full period of after now or half period behind and half after.
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 am skeptical about all functions with the _now
suffix in their name. within_period
sounds much better, but what you mention is also true; is it a half or full period before/after now?. ATM, I cannot think of anything better. I have looked at other APIs (specifically Clojure's clj-time and Java's joda-time) and I haven't seen anything much better. People will have to look at the documentation to make sure.
The other thing I was thinking is if the functions that return a bool
should start with is_
.
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.
Yeah, I don't have a better suggestion either. So no (more) complaining from me :)
I don't think these names are ambiguous enough to warrant the is_ prefix. They also live in a module called compare. But if you decide the is_ convention is something you want I support that too.
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.
Maybe it becomes clear if you change the name of "dt" to "start"?
A start and a period should be pretty unambiguous?
@@ -170,7 +172,7 @@ def verify(self, request, **kwargs): | |||
# verify username and password | |||
try: | |||
self._verify(_dict["password"][0], _dict["login"][0]) | |||
timestamp = str(int(time.mktime(time.gmtime()))) | |||
timestamp = saml2.datetime.utils.instant() |
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.
These kind of changes makes me happy. 👍
self.entities_descr.valid_until,)) | ||
except AttributeError: | ||
pass | ||
if self.entities_descr.valid_until is None: |
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.
Could this use the same flow as above?
if self.entities_descr.valid_until:
valid_until_date_time = saml2.datetime.parse(self.entities_descr.valid_until)
is_valid = saml2.datetime.compare.after_now(valid_until_date_time)
else:
is_valid = self.entities_descr.valid_until is None
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, it should ;)
raise ToEarly("Can't use response yet: (now=%s + slack=%d) " | ||
"<= notbefore=%s" % (now_str, slack, not_before)) | ||
""" | ||
It is valid if the current time is not earlier (before) than the nbefore |
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.
nbefore -> not_before
@@ -33,7 +33,7 @@ def test_duration(): | |||
assert valid_duration("-P1347M") | |||
assert valid_duration("P1Y2MT2.5H") | |||
|
|||
raises(NotValid, 'valid_duration("P-1347M")') | |||
# XXX raises(NotValid, 'valid_duration("P-1347M")') |
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 is ok to not support the "negative per element" duration as the XML xs:duration spec says it is invalid.
https://www.w3.org/TR/xmlschema-2/#duration
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 is right; however, aniso8601
does support it and thus we do too. This will change soon, as aniso8601
wants to strictly conform to ISO 8601 and negative durations will be dropped. You can read about this in issue#20. Until then, I am planning to change this comment to an expected failure.
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.
Cool, sounds like good decisions all around.
What is the status of this PR? I have another (small) changeset that adds support for explicit timezones in the date string parsing. However, this PR being merged would obviate the need for that work. My actual change is at https://github.com/IdentityPython/pysaml2/compare/master...CBitLabs:fix/explicit_offset?expand=1 |
Bump.... I'd really like to see this merged (once the conflicts are fixed). |
This Is a very important code refactor 🖖 |
…ts](../../pulls) for the same update/change? * [x] Have you added an explanation of what problem you are trying to solve with this PR? It is needed for returning attributes like ```` <saml:Attribute Name="dateOfBirth"> <saml:AttributeValue xsi:type="xs:date">2015-12-06</saml:AttributeValue> </saml:Attribute> ```` Otherwise I get an ValueError Exception like ```` Type and value do not match: date:<class 'str'>:2015-12-06 ```` because `type(value) is not valid_type == True` I didn't write any unit test, just tested in my context. Two important things: 1. This PR is on top of IdentityPython#597 but it doesn't have any dependencies on it. The only file I changed is saml.py, let me know if this PR will be merged in next releases and if you need separate branch for every features. In the future I will this way. 2. I think that the following code https://github.com/IdentityPython/pysaml2/pull/602/files#diff-6c156669cad61eda35e679329251dce9R197 could be also improved according to IdentityPython#518
Please can we get this merged...... (after fixing the conflicts) |
my 2 cents here since I haven't caught up with the discussion and the PR: When this PR was opened, latest version of package Package |
```` <saml:Attribute Name="dateOfBirth"> <saml:AttributeValue xsi:type="xs:date">2015-12-06</saml:AttributeValue> </saml:Attribute> ```` Otherwise I get an ValueError Exception like ```` Type and value do not match: date:<class 'str'>:2015-12-06 ```` because `type(value) is not valid_type == True` I didn't write any unit test, just tested in my context. I also think that the following code https://github.com/IdentityPython/pysaml2/pull/602/files#diff-6c156669cad61eda35e679329251dce9R197 could be also improved according to IdentityPython#518 if you agree.
This changeset introduces a new module, namely
saml2.datetime
. It implements the concept of datetime and duration object and isolates all operations on those, under a common namespace. The new module ensures that creation, conversion and operations on those objects are kept in one place, are correct and in accordance to the specs.The way the new module is written is such that it is not tied to a third party. The dependency should be easily swapped by filling in the required parsers; which should be as easy as changing the following two lines:
saml2/datetime/__init__.py#L48
andsaml2/datetime/duration.py#L5
aniso8601
was chosen to provide the datetime and duration parsers, as:Things to improve:
Motive for this was the discussion for issue #445 and the relevant thread on the mailing list. It overrides pull-request #517 .
Review is better done per commit. The reordering and cleanup of imports can be ignored.
Moreover, part of this PR is the introduction of two more modules:
compat.py
: a module that gathers all compatibility code between python3 and python2errors.py
: a module that gathers all errors for pysaml2. At the moment it provide the top-level generic errorSaml2Error
class. All errors in the library should be a subclass ofSaml2Error
.All Submissions: