https://github.com/openprinting/pyppd
A CUPS PostScript Printer Driver's compressor and generator
https://github.com/openprinting/pyppd
Last synced: 7 months ago
JSON representation
A CUPS PostScript Printer Driver's compressor and generator
- Host: GitHub
- URL: https://github.com/openprinting/pyppd
- Owner: OpenPrinting
- License: mit
- Created: 2012-06-15T16:05:00.000Z (over 13 years ago)
- Default Branch: master
- Last Pushed: 2025-04-30T22:17:11.000Z (8 months ago)
- Last Synced: 2025-04-30T23:25:40.154Z (8 months ago)
- Language: Python
- Homepage:
- Size: 113 KB
- Stars: 8
- Watchers: 5
- Forks: 10
- Open Issues: 0
-
Metadata Files:
- Readme: README
- License: LICENSE.txt
Awesome Lists containing this project
README
# pyppd
`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.
## Installation
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/).
## Usage
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
```
## Testing Framework
### Overview
`pyppd` uses a comprehensive testing approach that includes unit tests, integration tests, and end-to-end functionality tests. The test suite verifies:
1. **Component functionality**: Each module is tested in isolation to ensure it correctly performs its specific tasks
2. **Integration between components**: Tests verify that modules work correctly together
3. **End-to-end functionality**: Tests validate the complete workflow from archive creation to PPD extraction
4. **Command-line interface**: Ensures the CLI works as expected with various arguments
### Test Architecture
The test suite is organized as follows:
- `tests/test_archiver.py` - Tests for the PPD archive creation functionality
- `tests/test_compressor.py` - Tests for the compression and decompression functions
- `tests/test_ppd.py` - Tests for PPD file parsing functionality
- `tests/test_cli.py` - Tests for the command-line interface
- `tests/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.
### Setting Up a Test Environment
For the best testing experience, we recommend using a virtual environment:
```bash
# 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
```
### Running Tests
There are several ways to run the tests for pyppd. All methods allow you to run the tests without installing pyppd.
#### Method 1: Using the Makefile
The simplest way to run tests:
```bash
$ make -f Makefile.tests test
```
For verbose output:
```bash
$ make -f Makefile.tests test-verbose
```
To run just the unit tests:
```bash
$ make -f Makefile.tests test-unit
```
To clean test artifacts:
```bash
$ make -f Makefile.tests clean-test
```
#### Method 2: Using pytest directly
```bash
$ PYTHONPATH=. pytest tests/
```
For more detailed output:
```bash
$ PYTHONPATH=. pytest -v tests/
```
To run only specific test files:
```bash
$ PYTHONPATH=. pytest tests/test_archiver.py
```
#### Method 3: Using unittest
```bash
$ PYTHONPATH=. python3 -m unittest discover -s tests
```
To run specific test files:
```bash
$ PYTHONPATH=. python3 -m unittest tests/test_archiver.py
```
### Running Tests Outside the Source Tree
You can also run tests from outside the source directory:
```bash
$ PYTHONPATH=/path/to/pyppd pytest /path/to/pyppd/tests/
```
### Code Coverage
To check test coverage (requires the `pytest-cov` package):
```bash
$ pip install pytest-cov
$ PYTHONPATH=. pytest --cov=pyppd tests/
```
For a detailed HTML coverage report:
```bash
$ PYTHONPATH=. pytest --cov=pyppd --cov-report=html tests/
```
## Implementation Design
`pyppd` follows a modular design with these core components:
1. **PPD Parser** (`ppd.py`): Parses PPD files to extract printer model information and device IDs.
2. **Compression Engine** (`compressor.py`): Handles compression and decompression using the XZ binary.
3. **Archive Generator** (`archiver.py`): Creates self-extracting Python scripts that contain compressed PPD files with an index for quick access.
4. **Command Runner** (`runner.py`): Provides the command-line interface and handles user input.
The system works as follows:
1. 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
2. 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.
## Contributors
* **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.