-
Notifications
You must be signed in to change notification settings - Fork 130
Remove arrow, transition to attrs and add Timespan #222
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
Conversation
|
@N-Coder This really looks like you put a lot of thinking into that and seems really smart ! :) |
|
I also see that i don't have much time to handle ics.py at the moment and you seem very motivated to work on it. Would you like to have write rights on the repo ? I'd be glad to give them to you! |
Thanks!
Very good, then I'll try to completely get rid of Arrow.
Motivated yes, but I unfortunately don't have that much spare time either. 😅 |
|
@C4ptainCrunch do you have an Opinion on attrs / python 3.7 dataclasses? They would make declaring all our data container classes a lot easier and less repetitive, esp. they would autogenerate most of the constructors and prevent bugs like #217. |
|
Arrow is gone from the library code with 02a5904! So the things still left to do are:
Here's the updated table of affected issues from #155 (comment):
|
|
Hey 👋
I just gave you the rights to push on
This is awesome, thanks ! 👍 About
What do you think ? |
|
Thanks! 👍 Regarding Note: when doing the migration, watch out for CREATED vs DTSTAMP #200. |
Seems good to me !
Of course ! Ping me when you need help :) |
…() issues" This reverts commit ed14cde. See discussion here: #222 (comment) We want to only include simple changes in the directly next release and wait with the complicated all-day and event timestamp handling issues for the then following dedicated release, which is prepared in #222.
…() issues" (#231) This reverts commit ed14cde. See discussion here: #222 (comment) We want to only include simple changes in the directly next release and wait with the complicated all-day and event timestamp handling issues for the then following dedicated release, which is prepared in #222.
|
Yep, I wouldn't want to code without it anymore. I was actually also thinking whether |
This implementation collects all time-related attributes of an event - begin, end/duration and precision - in a single, isolated and easy to test class. It also targets the three possible cases for time values - timezone-aware datetimes, floating datetimes and all-day dates. Additionally to not doing anything implicitly (neither any fancy value conversion nor fancy indirect property access),it viciously guards the validity of its internal state. The validity of each of the 4 values representing the event time is highly depending upon the other values. Allowing mutation opens up easy ways of breaking validity - so the implementation is actually immutable, with each modification creating a new object and requiring a full revalidation of all 4 values together. As the times shouldn't be modified that often, having the optimization of mutability shouldn't be strictly necessary. Additionally, moving the functionality to its own class should make testing easier and keeps this quite complicated functionality clean from other concerns. All this makes the timespan handling easier to implement correctly, but unfortunately not easier to use. So I also started modifying the Event class to handle the interfacing in an user-friendly way and hide the complexity of timespans.
ToDo: - verify Timeline operation and Timespan Comparability (esp. with diverging timezones) - ensure that correct parse_date(time) is used - keep track of used_timezone information from serialize_datetime_to_contentline and generate VTIMEZONEs
Also, Event and Todo now share most of their code and Todo only renaming all the `end*` functions to `due`. Timespan finally has proper comparision by defining lt and eq based on (begin, end) and the remaining functions generated by @functools.total_ordering. Event and Todo also use the same ordering as _timespan is their first attribute. Also contains fixes for Timespans with no begin (which are considered floating) and small timedelta floating-point arithmetic errors.
@N-Coder If I understood you correctly, sure, that would be a good idea. Of course, examples would help a lot. The question is, where to include it into the documentation. As the PR hasn't been merged to master yet, it might get a little difficult to add this. 😉 |
|
The tool I was thinking about (but couldn't recall its name) was doctest. It simply takes any docstring in a python file or any other documentation text file, extracts all blocks of python interpreter sessions it finds, tries to run the exact same input against the latest version of the code again, and checks whether the actual output matches what is written in the documentation. |
I see what you mean. That's a good idea! Based on your idea, may I extend it? 😉 As we use pytest for testing, we could use this framework to do (doc)testing as well. You can still write your doctests. The best part is: you can do it in separate files, in your documentation, or both. What you need:
There is only one, minor issue: inside the documentation, you can only use a small part of the whole test (as an excerpt to show some things). But I don't think this is a big problem. You can still add What do you think? Would that be an idea to pursue? |
according to RFC 5545, clients should default to using local time for floating times, so all the complicated normalization logic is replaced by simply default to tzlocal()
|
That sounds very good! If was also thinking about using the Regarding the "minor issue", we anyways need to think about how to draw the line between doctests for introductory tutorials or advanced-user documentation (or in other locations with different purposes), expect-like In addition to my previous basic I guess my changes here are close to reaching a point where they are mature enough to be put into a
Would that new branch help? Could your rebase your doc PR onto that |
Hah, great! 👍 For more details, refer to https://docs.pytest.org/en/latest/doctest.html.
Right. It may be a matter of taste where to put it. I see the doctests in documentation more of having some good examples to help our users. As a nice side effect, we can test it. 😄 I did that with another project and it showed their benefits instantly: it helped me to discover typos and outdated information. That makes maintaining documentation a least a bit easier as it gives you feedback once there is something wrong.
You can do the conversion with pandoc automatically.
Sure, I can try. |
|
Okay, I guess the removal of
So there are also still quite a lot open points on the todo list (which should at some point maybe move to one or more meta-issues, together with the further topics mentioned towards the end of this comment), but I think this version should be stable enough that further work can be based on it. My next topic would be reworking the serializer and parser logic and getting the handling of timezones right while doing so, but I'd prefer to do that in a new PR. @C4ptainCrunch what do you think, can we merge this PR? Maybe, as suggested above, into a |
arrow, transition to attrs and add Timespan
arrow, transition to attrs and add Timespan|
Most important breaking changes:
|
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.
@N-Coder Hi Nico, not sure if this is helpful, but I had this lying around. 😉
I found some minor typo fixes here and there. Nothing serious. Also I'm not quite sure about the project's policy regarding docstrings for classes and functions. I haven't added a comment for all.
I hadn't had the time to look at the RST files. If you like, I can have a look at them too. 😉
Other than that, this PR is quite impressive! 👍
|
|
||
| class Calendar(Component): | ||
| @attr.s | ||
| class CalendarAttrs(Component): |
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.
Class docstring missing.
| from ics.alarm import * | ||
|
|
||
|
|
||
| class BaseAlarmParser(Parser): |
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.
Class docstring missing.
Co-Authored-By: Tom Schraitle <tomschr@users.noreply.github.com>
|
Thanks for your review! Honestly, I didn't really touch inline code docstrings. Especially the method docstrings are probably now pretty far from what actually happens in some places. I'm wondering whether it might be a good idea here to first start creating an overview over the big-picture functionality and only afterwards go into documenting the details on each method. There's probably also a lot of common terms / ideas that should be described first and then allow to keep the in-code documentation short. Regarding class documentation, let's start with the current class hierarchy as it is implemented: Entries marked with a star are mostly for internal usage and should be relevant for the public interface of the library. So, for public usage, the class hierarchy should look like this: I'm not quite sure how to properly convey this. Should we add I'm not sure how to tackle this, in terms of how to make the resulting documentation the most useful, how to realize this technically (e.g. also how to document attributes and possibly the generated init method and how to make this information transparently visible from subclasses) and probably also at which place to start (i.e. big picture documentation, class documentation, and detailed method/attribute docs). |
|
Thanks Nico for your detailed message. 👍
as all your questions are kind of related, I try to answer them from a different perspective:
These are just some ideas. Hope they make sense. 😉 Maybe it's easier to tackle doc relevant issues in another pull request to keep this PR easy and well-arranged. |
|
Thank you for your awesome work @N-Coder and for your review @tomschr
Let's go, i'm merging it ! I'll merge it in master though as it's destined to be the next version anyway :) |
This implementation collects all time-related attributes of an event - begin, end/duration and precision - in a single, isolated and easy to test class. It also targets the three possible cases for time values - timezone-aware datetimes, floating datetimes (which are currently unsupported, for details see further down) and all-day dates (which are currently pretty buggy). Additionally to not doing anything implicitly (neither any fancy value conversion nor fancy indirect property access), it viciously guards the validity of its internal state. The validity of each of the 4 values representing the event time is highly depending upon the other values. Allowing mutation opens up easy ways of breaking validity - so the implementation is actually immutable, with each modification creating a new object and requiring a full revalidation of all 4 values together. As the times shouldn't be modified that often, having the optimization of mutability shouldn't be strictly necessary. Additionally, moving the functionality to its own class makes testing easier and keeps this quite complicated functionality clean from other concerns. All this makes the timespan handling easier to implement correctly, but unfortunately not easier to use. So I also started modifying the Event class to handle the interfacing in an user-friendly way and hide the complexity of timespans.
This is by far not completely integrated in the rest of the library, as I first wanted to see where this approach might lead us and now also wanted to collect feedback. See below on how we might continue from hereon.
Where did Arrow go?
I first started out with the realisation that
floor('day')doesn't suffice for date values, as we need to get rid of timezone:Unsetting the timezone does strange things, as the constructor always interpolates
tzinfo=NonetoUTC:Trying to use arrow for naive (i.e. floating) timestamps by keeping
tzinfoset toNoneis thus very hard. So we would need another variable to keep track of whether thetzinfois intentionally set or should actually beNone. This would require a lot of special casing, e.g. when comparing a timestamp with timezone +3 with one with UTC, as we need to check whether UTC actually means floating.To summarize, I'm not quite happy with the automatic timezone mangling that arrow does. It might be useful to do things better when you don't worry about timezones, but here we have to actually get timezones right ourselves without something automagically mangling them. Python's built-in datetime might be annoying, because it is a lot harder to use right and tends to throw exceptions (e.g. when comparing timezone-aware datetimes with timezone-naive/floating datetimes). But in those special cases guessing right automatically is hard. Additionally, these exception point us at places where we might need to handle things explicitly - e.g. when mixing timezone-aware datetimes, floating datetimes and all-day dates that don't have a timezone.
As a result, for me, the selling points of arrow no longer outweigh its deficits. Too many modules and types is not an argument here, as this should be a library and not some prototype code that you are typing into the interpreter shell. Doing conversions automatically and not properly handling naive timestamps is the root cause for the above problems. And all the other features could also be implemented on top of plain old datetimes - actually the
Arrowclass is only a thin wrapper around thedatetimeclass, adding some functions but no further information (!). As a result, mytimespanimplementation only uses the plain, old, but reliabledatetimeclass.Further steps
So, I tried to do a timespan implementation based on datetime and tried to cover all edge-cases I could come up with. For those that I didn't think of, it should trow an exception so that we can think of the proper way of handling it once it comes up, instead of it vanishing through some magic conversion resulting in probably wrong results.
This should somewhat follow the zen of python (see
import this)And now finally
make_all_daylooks good - see the quite extensive test for this function. Additionally, this functionality would now allow floating times properly, as they are not that different from dates. Actually, it should fix all the issues referenced in #155 (comment). If we want to go this way, we would probably need to make changes in a few other places and think about breakages in the public API. But, this would also allow us to finally properly conserve floating events and VTIMEZONES when parsing and later exporting an .ics file. What also still needs testing is handling of events with different begin and end time zone - e.g. plane flights. But I'm confident that those will also work reliably, given that we are able to define the right expectations and guarantees we have/want in these cases.@C4ptainCrunch what's your opinion on this? Should we try to continue on this way, potentially removing arrow support in many places for the sake of correctness? I'd love to finally see this working properly and I could try to get a complete version with this new functionality working, if you also think that this is the way to go.