(Please note this is still a draft of my final blog)

And just like that, my Google Summer of Code journey with OpenPrinting is drawing to a close. This post serves as a final summary of my project: rebuilding pycups from the ground up for libcups3. While I still have a few things I plan to update, this covers the core of my work over the summer.

It’s been an incredible experience, and I’m excited to share the architectural decisions, challenges, and “magic” tricks that went into creating the new pycups.

The Problem and the Goal

The original pycups was a powerful C extension, but after 15 years, it was becoming difficult to maintain and, more importantly, to extend for the new libcups3. My GSoC proposal was to rewrite it, pitching a hybrid solution: use the CFFI (C Foreign Function Interface) to call libcups APIs directly from a library written almost entirely in pure Python.

The goal was to create a new pycups that is:

  • Easier to read and maintain.
  • Simple to extend for new libcups APIs.
  • Deeply “Pythonic,” abstracting away C-level complexity for the end-user.

The Foundation: CFFI and a New Architecture

The heart of the new design was a single, powerful Connection class. Early on, I realized that splitting logic by protocol (like HTTP, IPP, etc.) wasn’t feasible, as CUPS does almost everything over HTTP. Instead, I took inspiration from pycups2 and used Mixin classes to inject methods into the main Connection class, keeping the codebase organized and avoiding a single 10,000-line monster file. But, the monster class, really became a monster. So, we are now having a different approach, but the foundation remains the same.

This foundation rests on three core pillars.

Pillar 1: Pythonic Wrappers for C Structures

A major part of the project was representing C structures in a way that feels natural to a Python developer. Instead of forcing users to manage C-level memory, I created Python classes that wrap these structures.

The cups_dest_t C structure, for example, is represented by a Python cupsDest class. This class (and others like it) handles memory allocation (cffi_new) and freeing (cffi_free) behind the scenes.

More recently, this philosophy was extended to the core HTTP components. I’ve been busy implementing more HTTP-related APIs and creating classes for essential C structures like http_t, http_field_t, and http_addr_t.


Pillar 2: Intelligent and Abstracted Enums

Working with C often means dealing with macros and “magic numbers.” I simplified this by mapping C enums to Python’s native IntFlag enums. This allows for readable, type-safe code.

Before (C-style): conn.encryption = 3 After (Pythonic): conn.encryption = HttpEncryption.ALWAYS

But we went a step further. Using Python’s “dunder” (magic) methods, we abstracted away the C-to-Python boundary:

  1. __str__: Calling str(IPPOp.PRINT_JOB) doesn’t return "IPPOp.PRINT_JOB". Instead, it’s hijacked to call the C API lib.ippOpString, returning the actual IPP string, "Print-Job".
  2. _missing_: This class method allows the reverse. A user can instantiate an enum from a C-string (IPPOp("Print-Job")), and pycups will automatically call the C API lib.ippOpValue to map it back to the correct enum member.

This work was also expanded to include other critical enums like http_encoding_t and http_state_t.


Pillar 3: The “Intelligence” of On-Demand Access

Perhaps the “smartest” part of the new pycups is that it doesn’t block you. If I haven’t written a pretty Python wrapper for a libcups3 API yet, you can still use it. The CFFI bridge (cups.lib and cups.ffi) is exposed, allowing direct calls to the underlying C library.

This “on-demand” philosophy extends to data. When you access a property on an object (like an IPP attribute), pycups uses the @property decorator to call the C getter (e.g., ippGetString) at that exact moment. There’s no data caching in Python, which means what you see is always the live C data.

A great example of this, thanks to my mentor Callahan Covacs, was using the struct library to parse a 7-byte C hex date from ippGetDate directly into a Python datetime object (struct.unpack(">H5B", ...))—a perfect blend of C power and Python elegance.


Recent Work: Simplifying the Developer Experience

My most recent work moved beyond plain API implementation and into the developer experience of the library itself. I was focused on simplifying class initializers and finding a way to get “almost true” method overloading in Python.

After researching custom modules like multiple-dispatch , I found a very simple and clean solution using the singledispatch APIs from Python’s standard functools library. This allows for much cleaner initialization logic without the overhead of external dependencies. I’m excited about this and will be writing a separate blog post to elaborate on the design, as it touches on some fundamental concepts of object-oriented programming.


Conclusion and What’s Next

The result of this summer’s work is a new pycups that is maintainable, extensible, and a joy to use. It smoothly bridges the C and Python worlds, hiding the complexity of libcups behind a friendly, Pythonic-first API.

While this marks the end of my GSoC term, I’m not quite finished. As I mentioned, I’ll be updating the project with some more work that’s left to do. My future goals include,

  • Getting the library properly tested
  • Implementing this library in the system-config-printer
  • Implementing PyCups centric Errors, bugs and Exceptions for easy debugging
  • Fixing all the linter issues
  • Looking into the possiblity of writing unit tests, because as of now, we cannot really mock a printer right?
  • Setting up CI and CD for up-to-date sync with libcups
  • Helping libcups with quashing all the bugs found in the docs and library

To know more about my work in details, read my other blog posts:

  1. GSOC: PyCups3 is Abstracting!
  2. GSOC: PyCups3 is intelligent?
  3. GSOC: Until Midterm, August 2025

I want to extend a massive thank you to my mentors, Callahan Covacs and Bhavanishankar Ravindra, for their invaluable guidance, and to the entire OpenPrinting organization for giving me this incredible opportunity.

So, take up cups and Stay caffeinated! ☕ Cheers Till!