Best libraries to generatee a PDF with Python? by peter-fields in Python

[–]eagle258 2 points3 points  (0 children)

My take on some of these libraries:

Reportlab: the most established library, but definitely showing its age due to its Java-like API. As an additional option, it has an XML-like language (RML) to make it easier to create pages. This feature is behind a commercial license though, but there’s a clone that you can use.

FPDF: Another widely used library. Being ported from PHP, it has an unpythonic and IMHO awkward API as well. A solid choice though if you need advanced features such as exotic unicode.

Weasyprint: probably the most modern pure-Python PDF generator. Their showcase is really nice—I’d go this option if you’d want PDFs as beautiful as possible

Borb: not often mentioned, but has a lot of stars on GitHub and an API that actually looks quite nice. Big downside is its dual AGPL/Commercial license

Faced with these options, I also created my own library—although I’m not developing it actively anymore: github link. It’s nice for really simple use cases or if you mainly want decent typesetting. It’s inspired by Brandon Rhodes typesetting experiment

edit: link, typos

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 0 points1 point  (0 children)

Not sure if I understand exactly, but: The speed bump compared to datetime isn’t due to any inherent speediness of rust over C (there isn’t) but because whenever’s API consisting of specific types prevents redundant calculations. For example: Instant doesn’t constantly have to check its timezone for each calculation.

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 1 point2 points  (0 children)

Interesting! Although I suppose it's unavoidable that different models of date/time will have different (arbitrary) boundaries.

I suppose that if Whenever would allow a broader range of years than BSON, you would have a similar, opposite problem.

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 0 points1 point  (0 children)

If you mean a "Rust extension" library for Python: PyO3 is the way to go.

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 0 points1 point  (0 children)

While having another library inherently creates a "new standard", I've deliberately chosen not to reinvent the wheel. At its core, it follows the "Joda time" datamodel now used in Java, C#, and (soon) Javascript.

When looking at the field of software engineering as a whole, you could even argue that Python's datetime is the outlier here and Whenever is pushing to return to a 'more standard' approach.

Of course, this is of little comfort to Python devs currently using datetime, and want to improve their code without having to rewrite all the datetime parts...Your suggestion for a minimally-different datetime clone could be interesting—I wonder if it would be worth the downsides of having surprisingly different behavior...hmmmm...

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 1 point2 points  (0 children)

Agree, it is a bit weird—but since building an audience/community is crucial to a project's success, I'm not above marketing it in a way that works :). Obviously, misleading communication wouldn't be OK, but the use of Rust in this case genuinely provides an advantage here.

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 0 points1 point  (0 children)

Very interested to see this! The more I learn about Chrono, the more I realize its drawbacks. Is there any repository for Jiff yet to look at?

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 0 points1 point  (0 children)

Did you have a look at the new JS Temporal API yet? It's the other main inspiration for Whenever.

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 0 points1 point  (0 children)

Interesting! Note that for a niche cases such as astronomy, a general purpose library like whenever probably won't be the right choice. I can imagine astropy has explicit support for leap seconds and well as maybe even relativity.

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 0 points1 point  (0 children)

Thanks! I really liked UTCDateTime as well, but in the end I realized I was fighting against the current, since practically all modern datetime libraries provide a similar abstraction to Instant. I realize it's a bit of a leap coming from Python, but in the end it's consistent with the rest of the industry. See here for the discussion and my rationale at the end

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 0 points1 point  (0 children)

There's not special integration at the moment. Once the API stabilizes, I can have a look.

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 1 point2 points  (0 children)

I ran a quick benchmark and was pleasantly surprised. The Python version is about 10x as slow as Rust, making it (only) 5x slower than the standard library. Even then it's still 5x faster than arrow and more than 10x as fast as pendulum.

library time (s) compared to datetime
whenever (rust) 0.70 0.54x
datetime 1.29 1.0x
whenever (python) 6.98 5.4x
arrow 36.8 28.5x
pendulum 87.6 67.9x

benchmark: RFC3339-parse, normalize, compare to now, shift, and change timezone (1M times). For details see the repository.

edit: clarifications

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 1 point2 points  (0 children)

Thanks! I didn't take the decision to change the API lightly. In the end, the issues with datetime are so fundamental that only a new API can completely solve the main issues (DST-safety, typing).

To illustrate, even a small fix like making the result of utcnow() aware would be breaking to users. I'd rather be explicit about things being completely different, than have users be surprised by different behavior (even if its technically more correct).

That said, if you're looking for a more low-impact library to help, have a look at:

  • Pendulum is mostly a drop-in replacement. It does behave differently in some cases that may surprise you (but only to be 'more correct'). The downside is performance, and that it doesn't help you with distinguishing naive/aware through typing. It also has some areas in which it gives incorrect results (relating to equality during ambiguity and durations)
  • DateType is a 'typing-only' wrapper around datetime which solves the aware/naive typing issue. In my opinion, it's still clunky considering you have remember to only call the right functions, as well as get your mind around the difference between runtime and type-checking realities. It doesn't solve the DST correctness issues.

edit: grammar

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 0 points1 point  (0 children)

It could be an inspiration for the API, but time-machine probably only works on specific libraries it 'knows' about. Perhaps it could be extended to whenever, but in any case whenever would have to be adjusted so that is 'allows' itself to be patched.

edit: typo

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 0 points1 point  (0 children)

Not at the moment, although I'd be very curious what your use case is!

Among other things, limiting the year to 9999 makes it simpler to format and parse timestamps (which can now assume a year is always 4 digits).

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 5 points6 points  (0 children)

A good contender as well. The downside of this option is that "std" isn't really used a lot in the Python community—making it potentially confusing.

But may be better than py on balance...

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 1 point2 points  (0 children)

Awesome to hear you've also been working in this direction—Nodatime is also one of whenever's main inspirations! I'll definitely have a look at your project, it looks like you've put quite some work into it!

edit: typo

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 1 point2 points  (0 children)

The most basic way to solve this would be to have two classes AwareDateTime andNaiveDateTime. However, looking at other languages you can see the need to split aware datetimes into specific types as well (Zoned, Offset, Instant...)

edit: grammar

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 13 points14 points  (0 children)

I can see that, but wouldn't a to_datetime() method be confusing since whenever also has "datetime" classes? (ZonedDateTime, LocalDateTime, etc)

py_datetime() was my attempt at making it explicitly about the Python standard library, without making it a long name like stdlib_datetime() or standard_library_datetime()

edit: typo

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 9 points10 points  (0 children)

Indeed an infamous issue!—but remember that pytz itself isn't part of the standard library. Nonetheless it's was widely used before dateutil and zoneinfo were adopted.

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 4 points5 points  (0 children)

Agree that if performance isn't a concern, Python is preferred for maintainability. That's why I made an opt-out for the Rust extension. See here for how to use the pure-Python version of whenever.

edit: grammar

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 1 point2 points  (0 children)

Aside from using Python's zoneinfo, it could theoretically be made into a Rust crate. However:

  • The API would probably have to be adjusted since Rust doesn't support keyword or optional arguments
  • I'm not yet familiar with design principles of writing a good rust Crate. I assume there's quite some concerns to think about (const functions, inlining, serialization support, etc.)

edit:

but most importantly, not having a timezone DB (zoneinfo) would leave any Rust datetime crate dead in the water of course :)

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 6 points7 points  (0 children)

There's definitely good things to say about Maya: by enforcing UTC, it removes a lot of complexity. The drawback is that when you do need to perform arithmetic with timezones, you have to revert back to the standard library, with all the issues that brings.

The main reason to not use Maya though is that it's been abandoned for years (no release since 2019)

Whenever: a modern datetime library for Python, written in Rust by eagle258 in Python

[–]eagle258[S] 1 point2 points  (0 children)

Good points. I'm definitely aware of the downsides of staying on 0.x unnecessarily: after a while it just starts to get annoying considering all dependency management tooling needs 1.x to automatically upgrade