pyppd
is a CUPS PPD generator. It holds a compressed archive of PPDs, which can be listed and retrieved only when needed by CUPS, saving disk space.
To install pyppd
, you can use:
# pip install pyppd
Or download the source package, uncompress, and run as root:
# python3 setup.py install
It depends on Python 3.x (http://www.python.org) and XZ Utils (http://tukaani.org/xz/).
At first, you have to create a PPD archive. For such, put all PPDs (they might be gzipped) you want to add in the archive inside a single folder (which can have subfolders), then run:
$ pyppd /path/to/your/ppd/folder
It'll create pyppd-ppdfile
in your current folder. This executable only works with the same Python version that you used to generate it. You can test it by running:
$ ./pyppd-ppdfile list
And, for reading a PPD from the archive, simply do:
$ ./pyppd-ppdfile cat pyppd-ppdfile:MY-PPD-FILE.PPD
For CUPS to be able to use your newly-created archive, copy pyppd-ppdfile
to /usr/lib/cups/driver/
and you're done.
The generated pyppd-ppdfile
can be arbitrarily renamed, so that more than one packed repository can be installed on one system. This can be useful if you need better performance, be it in time or memory usage. Note that also the PPD URIs will follow the new name:
$ ./pyppd-ppdfile list
pyppd-ppdfile:LasterStar/LaserStar-XX100.ppd
$ mv pyppd-ppdfile laserstar
$ ./laserstar list
laserstar:LaserStar/LaserStar-XX100.ppd
pyppd
uses a comprehensive testing approach that includes unit tests, integration tests, and end-to-end functionality tests. The test suite verifies:
- Component functionality: Each module is tested in isolation to ensure it correctly performs its specific tasks
- Integration between components: Tests verify that modules work correctly together
- End-to-end functionality: Tests validate the complete workflow from archive creation to PPD extraction
- Command-line interface: Ensures the CLI works as expected with various arguments
The test suite is organized as follows:
tests/test_archiver.py
- Tests for the PPD archive creation functionalitytests/test_compressor.py
- Tests for the compression and decompression functionstests/test_ppd.py
- Tests for PPD file parsing functionalitytests/test_cli.py
- Tests for the command-line interfacetests/test_integration.py
- End-to-end tests verifying the complete workflow
The tests use temporary directories and sample PPD files to create controlled environments for testing. This approach ensures that tests can run without affecting the system and can be executed in any environment.
For the best testing experience, we recommend using a virtual environment:
# Create a virtual environment
python -m venv pyppd-env
# Activate the virtual environment
# On Linux/macOS:
source pyppd-env/bin/activate
# On Windows:
pyppd-env\Scripts\activate
# Install development dependencies
pip install pytest
There are several ways to run the tests for pyppd. All methods allow you to run the tests without installing pyppd.
The simplest way to run tests:
$ make -f Makefile.tests test
For verbose output:
$ make -f Makefile.tests test-verbose
To run just the unit tests:
$ make -f Makefile.tests test-unit
To clean test artifacts:
$ make -f Makefile.tests clean-test
$ PYTHONPATH=. pytest tests/
For more detailed output:
$ PYTHONPATH=. pytest -v tests/
To run only specific test files:
$ PYTHONPATH=. pytest tests/test_archiver.py
$ PYTHONPATH=. python3 -m unittest discover -s tests
To run specific test files:
$ PYTHONPATH=. python3 -m unittest tests/test_archiver.py
You can also run tests from outside the source directory:
$ PYTHONPATH=/path/to/pyppd pytest /path/to/pyppd/tests/
To check test coverage (requires the pytest-cov
package):
$ pip install pytest-cov
$ PYTHONPATH=. pytest --cov=pyppd tests/
For a detailed HTML coverage report:
$ PYTHONPATH=. pytest --cov=pyppd --cov-report=html tests/
pyppd
follows a modular design with these core components:
-
PPD Parser (
ppd.py
): Parses PPD files to extract printer model information and device IDs. -
Compression Engine (
compressor.py
): Handles compression and decompression using the XZ binary. -
Archive Generator (
archiver.py
): Creates self-extracting Python scripts that contain compressed PPD files with an index for quick access. -
Command Runner (
runner.py
): Provides the command-line interface and handles user input.
The system works as follows:
-
During archive creation:
- PPD files are collected from the specified directory
- Each PPD is parsed to extract metadata
- All PPDs are concatenated into a single byte array
- The byte array is compressed with XZ
- A JSON index is created mapping printer models to their position in the compressed archive
- The compressed archive and index are embedded in a Python script template
-
During PPD extraction:
- The index is decompressed and loaded
- The requested PPD's position is looked up in the index
- Only the required portion of the archive is decompressed
- The PPD is returned to standard output
This design minimizes memory usage while allowing fast access to individual PPD files without decompressing the entire archive.
- Till Kamppeter - Original idea, mentoring and feedback. User #0.
- Hin-Tak Leung - Lots of technical suggestions.
- Martin Pitt - Python 3 port.
- Flávio Ribeiro and Diógenes Fernandes - Refactorings and general Python's best practices tips.
- Didier Raboud - Make archives reproducible, by sorting the list of found PPDs and using JSON dumps instead of Pickle dumps.
- Sambhav Dusad - Streaming decompression, to not need to hold the whole decompressed archive in memory.
- Google's OSPO - Initial funding at GSoC 2010.