v1.0.2 (2013-09-12) * setup.py, pyppd/runner.py, pyppd/pyppd-ppdfile.in: Changing version to 1.0.2 * MANIFEST.in: Without it, the ISSUES aren't packed in the distribution file, and setup.py breaks as it needs it to generate the long_description. * contrib/git2changes.py: Fix git2changes when commits only modified one file v1.0.1 (2013-09-05) * setup.py, pyppd/runner.py, pyppd/pyppd-ppdfile.in: Changing version to 1.0.1 * pyppd/ppd.py: - if a device ID has no MDL/MODEL field, it is not taken into account as an extra model - If in a device ID a DRV: field is found (Foomatic info) this is added to the device IDs of all model entries. - Model names get normalized before comparing: all lowercase, leading and trailing white space stripped, manufacturer name in the beginning removed, then only really different models get an extra entry. - If a PPD contains one or more "*1284DeviceID:" lines and only one "*Product:" This removes tons of bogus lines, especially in the PPD from Foomatic )both generated from XML and ready-made PPDs from printer manufacturers) and HPLIP.1 v1.0.0 (2012-06-15) * setup.py, pyppd/runner.py: pyppd is being used for a couple years now, so it is stable. And, with Martin Pitt's patches (thanks!), it's working for Python 3. So I guess it deserves this big version bump :) * README: They have to have 4 spaces to be treated as code. * setup.py, ISSUES.txt, ISSUES: Renaming ISSUES.txt -> ISSUES. * setup.py, README.txt, README.md, README: GitHub only parses as Markdown if it's README.md, but PyPI complains if it's neither README nor README.txt. To make everyone happy, I symlinked. * setup.py: Changing repository URL to github. * README.txt: Updating README.txt about the Python 3.x support. * setup.py: Changing pypi status to Production/Stable. * setup.py, LICENSE.txt: This might make pyppd more useful for the *BSD guys. * setup.py: setup.py build crashed with UnicodeDecodeError, as open() defaults to the system locale, and README.txt contains non-ASCII characters. * pyppd/runner.py, pyppd/pyppd-ppdfile.in, pyppd/ppd.py, pyppd/compressor.py, pyppd/archiver.py: Use bytes for reading and writing the archive and PPD files. Do not try to decode/encode whole PPDs. Many of them are not in UTF-8, and not even in the encoding they claim to be in, so it's best to not assume anything about their encoding at all and just keep them as bytes. Only decode the minimal pieces This makes pyppd work with Python 3 and is also more robust against encoding Avoid calling str() on a PPD object, as that will cause UTF-8 encoding again on an unicode object. Just call __str__() directly. This is a bit ugly, but works with both Python 2 and 3. * setup.py, pyppd/pyppd-ppdfile.in, pyppd/ppd.py, pyppd/archiver.py: Fix syntax to be Python 3 compatible. * pyppd/runner.py, pyppd/pyppd-ppdfile.in, pyppd/archiver.py: Fix import statements to be Python 3 compatible. * pyppd/pyppd-ppdfile.in: Only exit if the IOError caught was a Broken Pipe. * pyppd/pyppd-ppdfile.in, bin/pyppd: 1. pyppd-ppdfile now catches IO errors at the actual print commands and not globally for the whole program. So real bugs will not be masked by the 2. pyppd catches only KeyboardInterrupt as it is writing to an actual disk file and so here the crash on broken pipe problem does not exist (a better error handling would still be nice though). v0.4.9 (2010-09-21) * setup.py, pyppd/runner.py, pyppd/pyppd-ppdfile.in: Changing version to 0.4.9 * pyppd/pyppd-ppdfile.in, bin/pyppd: KeyboardInterrupts are raised when the user uses, for example, CTRL+C. But when you do "./pyppd-ppdfile list | less" and, before the end of file, types "q", it raises an IOError. We don't want neither throwing a traceback into stdout. Thanks for the tip, Till. * bin/pyppd: Oops! Error in the indentation. v0.4.8 (2010-09-20) * setup.py, pyppd/runner.py, pyppd/pyppd-ppdfile.in: Changing version to 0.4.8 * pyppd/pyppd-ppdfile.in, bin/pyppd: As noted by Till Kamppeter, generating a traceback triggers an automatic bug report feature in some distributions (like Ubuntu). We avoid generating false bug reports by suppressing those KeyboardInterrupt's tracebacks. v0.4.7 (2010-09-20) * setup.py, pyppd/runner.py, pyppd/pyppd-ppdfile.in: Changing version to 0.4.7 * setup.py: Removing useless whitespaces. * contrib/git2changes.py: Removes ending whitespace in CHANGES.txt's commit messages. * pyppd/pyppd-ppdfile.in, pyppd/ppd.py: Make sure that the "pyppd-ppdfile list" output does not contain two lines with the same PPD URI * ISSUES.txt: Adds missing period. v0.4.6 (2010-08-17) * setup.py, pyppd/runner.py, pyppd/pyppd-ppdfile.in: Changing version to 0.4.6 * setup.py: If we don't do this, the resulting string isn't valid rST. v0.4.5 (2010-08-17) * setup.py, pyppd/runner.py, pyppd/pyppd-ppdfile.in: Changing version to 0.4.5 * ISSUES.txt: Adds "Generate manpage" issue. * setup.py: Letting our issues be known to everyone might encourage contributions. * ISSUES.txt: Minor esthetic changes. * README.txt: Adds Till, Hin-Tak, Flávio, Diógenes and OSPO as Contributors. * README.txt: Adds # and $ to indicate commands done in the shell. * README.txt: Adds multiple archives usage's instructions. v0.4.4 (2010-08-11) * setup.py, pyppd/runner.py, pyppd/pyppd-ppdfile.in: Changing version to 0.4.4 * pyppd/pyppd-ppdfile.in, pyppd/ppd.py: 1. The PPD parser confused "NickName" and "ShortNickName". This leads to the "ShortNickName" being used in "pyppd-ppdfile list" and so important information is missing in the listings and the listings are 2. "pyppd-ppdfile list" lists PPD URIs with the full path of pyppd-ppdfile, usually /usr/lib/cups/driver/pyppd-ppdfile. This makes the PPD generator not working correctly with CUPS. v0.4.3 (2010-08-10) * setup.py, pyppd/runner.py, pyppd/pyppd-ppdfile.in: Changing version to 0.4.3 * pyppd/ppd.py, pyppd/archiver.py: Renaming filename -> uri. * pyppd/archiver.py: So we avoid the problem of having PPDs with the same filename. It's working as: if I call pyppd with the folder /usr/share/ppd, it removes this part of each PPD path and uses the rest as the URI. For example, /usr/share/ppd/cups-included/textonly.ppd.gz will have the URI "cups-included/textonly.ppd". v0.4.2 (2010-08-09) * setup.py, pyppd/runner.py, pyppd/pyppd-ppdfile.in: Changing version to 0.4.2 * pyppd/archiver.py: Oops! Forgot to change the variable name. * setup.py: Adds classifiers to setup.py. * ISSUES.txt: Removing resolved issue. * pyppd/runner.py: I don't think logging to a file is worthwile to pyppd, as it isn't a daemon. If you encounter some problem, you'll be running it yourself, Also, I prefer using -v and -vv instead of -v and -d. I think it makes things more standard. * pyppd/runner.py, pyppd/ppd.py, pyppd/archiver.py, ISSUES.txt: Now, pyppd prints to stdout the WARNING, ERROR and FATAL messages, and logs those and every other to a log file (defaults to pyppd.log), which has a size limit of 2 MB. You can use --verbose and --debug to make pyppd more verbose. v0.4.1 (2010-08-09) * setup.py, pyppd/runner.py, pyppd/pyppd-ppdfile.in: Changing version to 0.4.1 * pyppd/archiver.py: When we have two PPDs with the same filename, we create add a random number to one of them, so we can add both to our archive. But, if this PPD which'll have the new name have more than one entry in the list, each entry will have a different random name. This commit fixes this. * ISSUES.txt: Adds new issue. * ISSUES.txt: "setup.py install" shouldn't link pyppd to /usr/lib/cups/driver. What should be there isn't pyppd, but the archive generated by it. v0.4.0 (2010-08-09) * setup.py, pyppd/runner.py, pyppd/pyppd-ppdfile.in: Changing version to 0.4.0 * pyppd/ppd.py: Now, when parsing a PPD, it adds as many *1284DeviceID lines as it can find. Afterwards, it looks for *Product lines. If found, check if it's unique (if there wasn't an *1284DeviceID line with the same product model), and adds it if so. * pyppd/archiver.py, ISSUES.txt: If we found a PPD with the same filename, we simply add a random number (between 0 and 99) to the end of its filename. * pyppd/pyppd-ppdfile.in: Renaming index -> value. v0.3.0 (2010-08-07) * setup.py, pyppd/runner.py, pyppd/pyppd-ppdfile.in: Changing version to 0.3.0 * pyppd/ppd.py, pyppd/archiver.py, ISSUES.txt: There are some of PPDs which are in different files but contains the same "*PCFileName:". So, without this change, pyppd overwritten them in the index, making them impossible to recover. Now, it'll only happen when there're two PPDs with the same basename. I'm also writing at ISSUES.txt that we need to figure out how to solve this possible problem. * pyppd/ppd.py: Fixing typo deviceid -> deviceid_re. * pyppd/ppd.py, pyppd/archiver.py: Renaming name -> filename. * pyppd/pyppd-ppdfile.in, pyppd/ppd.py, pyppd/archiver.py: As Till Kamppeter pointed out, a single PPD might have multiple descriptions, following the rules: a) If the PPD has an "*1284DeviceID:" line, create a single list entry with this device ID; b) If the PPD has one or more "*Product:" lines, create a pseudo device ID using the content of the "*Manufacturer:" line as manufacturer and "*Product:" line as model, whichout the parentheses. * ISSUES.txt: Removing "support for .ppd.gz" from our ISSUES list. v0.2.2 (2010-08-07) * setup.py, pyppd/runner.py, pyppd/pyppd-ppdfile.in: Changing version to 0.2.2 * pyppd/archiver.py, README.txt: Now, instead of looking for only *.ppd in the directory passed as parameter, it looks also for *.ppd.gz. When found, it tests: does it ends with ".gz" (case-sensitive (I have to change this))? If so, opens with gzip. If not, opens as usual. When we archive gzipped PPDs, we save them uncompressed. * pyppd/archiver.py: Refactoring some variable names. v0.2.1 (2010-08-07) * setup.py, pyppd/runner.py, pyppd/pyppd-ppdfile.in: Changing version to 0.2.1 * pyppd/archiver.py: Updating archiver.compress() documentation. v0.2.0 (2010-08-07) * setup.py, pyppd/runner.py, pyppd/pyppd-ppdfile.in: Changing version to 0.2.0 * pyppd/archiver.py: Removing useless try/except block. * pyppd/pyppd-ppdfile.in, pyppd/archiver.py: Instead of creating a .tar archive, we simply concatenate all PPDs found in a big text file, and save each's start and length inside of it. Then, when we have to "cat" it, we decompress the text file and print only In my tests, it's around 8% faster than using .tar. Also, it makes it easier, in the future, to support not only files .ppd, but also .ppd.gz. * pyppd/runner.py, pyppd/archiver.py: Rename method create_archive -> archive * pyppd/archiver.py: Reorganizing methods, the most important at the top * pyppd/compressor.py: Updating decompress() documentation * pyppd/pyppd-ppdfile.in, pyppd/lzma_proxy.py, pyppd/compressor.py, pyppd/archiver.py: Renaming lzma_proxy.py to compressor.py * pyppd/runner.py, pyppd/compressor.py, pyppd/archiver.py: The runner.py shouldn't have to use compressor.py. It should simply ask archiver.py to create the archive. Compression is an implementation detail of archiver. * pyppd/runner.py, pyppd/pyppd-ppdfile.in: Fixing version number 0.1.0 -> 0.1.6 * pyppd/ppd.py: Removing space between DeviceID's field name and content. * setup.py, contrib/git2changes.py: I made git2changes which simply gets the commit messages and organizes them into something similar to the GNU ChangeLog style. But, instead of separating the commits by day, I organize them by version. I think this way, even as it's not standard, generates a file that is more useful to pyppd's users. * CHANGES.txt, .gitignore: As CHANGES.txt is created from the git log, we have no use to maintain it under version control. v0.1.6 (2010-08-03) * setup.py: Changing version to 0.1.6 * MANIFEST.in: So, pyppd/pyppd-ppdfile.in gets into the distribution archive. * pyppd/pyppd-ppdfile.in: As now we don't uncompress the .tar.xz archive at ls(), when we tested if ppd.__class__ == tarfile.TarFile, it always failed. Now, at the iteration, we get the key AND the value (ppd). If the key is ARCHIVE, we continue. * ISSUES.txt: Adding Till's suggestions and some ideas to ISSUES.txt * setup.py: Making setup.py executable v0.1.5 (2010-08-01) * setup.py: Adding a description and bumping version to 0.1.4 v0.1.4 (2010-08-01) * setup.py: Changing version to 0.1.4 * README.txt: Adding instructions to install with pip v0.1.3 (2010-08-01) * setup.py: Changing version to 0.1.3 * pyppd/compressor.py: Instead of silently ignoring exceptions, raises them * README.txt: Minor reStructuredText fixes v0.1.1 (2010-08-01) * setup.py: Renaming the license in setup.py to GPLv3 * setup.py: Forgot to add read() to the long_description * setup.py, README.txt: Adding a README.txt and setting it as long_description * pyppd/pyppd-ppdfile.in: When you tried ./pyppd-ppdfile cat my-ppd.ppd, it raised a IndexError, as it tried to "my-ppd.ppd".split(":")[1], which don't exists. Now, instead of trying to read the second element ([1]), we read the last element, which works in both cases. * setup.py, pyppd/test/__init__.py: Remove pyppd/test, as I am not using tests (yet) * ISSUES.txt: As Gitorious don't offers us one. Maybe we should consider switching to github or something... * pyppd/pyppd-ppdfile.in: As the file lzma_proxy.py is inserted into pyppd-compressor, we don't use "import lzma_proxy as lzma". So, the methods are in the local namespace, thus we use compress/decompress, and not lzma.compress/decompress. * pyppd/lzma_proxy.py: Now, I need to change it's name. Lzma_proxy seems unnatural to me, and we already use compressor for another file. Maybe xz.py? * pyppd/pyppd-ppdfile.in: We used to decompress the PPDs in load(). So, even if the user simply wanted to list the PPDs (which we use only the index), he had to decompress everything. Now, only when the user asks to cat a file, we decompress the PPDs. * pyppd/pyppd-ppdfile.in, pyppd/compressor.py: This way, we compress 212mb to 3.2mb, instead of 8,2mb. This makes the program a bit slower (0.2s in my tests), but I think it's worthy. * pyppd/runner.py, pyppd/pyppd-ppdfile.in: Adding the \ to concatenate the multiline strings * pyppd/pyppd-ppdfile.in: Checks if pyppd-ppdfile is run directly and, if so, run main() * pyppd/pyppd-ppdfile.in: The name 'list()' is used for instantiate list objects. * pyppd/ppd.py: Ppd: Add object inheritance * pyppd/runner.py, pyppd/pyppd-ppdfile.in: Fitting to 80 columns * pyppd/archiver.py: Archiver: Removes unused import * pyppd/compressor.py: When we add the PPDs to the .tar file, /usr/share/ppd becomes usr/share/ppd. This way, we were unable to find it afterwards. Now, we convert every path passed as argument to absolute, and simply ignore the first slash when creating the index. * pyppd/ppd.py: Removes the "(" and ")" from the Product line * pyppd/ppd.py: Use Product instead of NickName in the 1284DeviceID fallback * pyppd/pyppd-ppdfile.in, pyppd/compressor.py: It's better than the sqlite3 solution because we can decompress and load the tar file from memory, without the need to write to decompress, write As tar doesn't have an index, it needs to do a sequential search to find the files. It might be better to use 7z instead. * pyppd/lzma_proxy.py: Adds compress_file(path), which compresses path with xz binary * pyppd/compressor.py: This way, sqlite3 indexes its values, making the selects much faster. * pyppd/pyppd-ppdfile.in, pyppd/compressor.py: It's much faster to load the sqlite3 DB and begin issuing SQL statements than to load the .sql dump into a new sqlite3 DB. * pyppd/pyppd-ppdfile.in, pyppd/compressor.py: cPickle uses too much memory when pickling/unpickling. And, as we work with large files, it becomes a problem. SQLite3 is more efficient in this regard. * pyppd/runner.py: Fixing bug of undefined variable when running pyppd with unexistent directory as argument * pyppd/ppd.py: This is probably wrong [1] but, as I found many PPDs which 1284DeviceID [1] http://www.undocprint.org/formats/communication_protocols/ieee_1284#ieee_1284_device_ * pyppd/compressor.py: Sets ppds = None when we don't need it anymore, trying to keep memory usage low. * pyppd/ppd.py: Adding languages not found in ISO 639, but found in some PPDs * pyppd/ppd.py: Converts PPD's language name to it's ISO 639 code * pyppd/ppd.py, pyppd/compressor.py: When compressing, ignore the PPDs which couldn't be parsed. * pyppd/pyppd-ppdfile.in, pyppd/ppd.py: Instead of having the PPDs names hardcoded with the executable's, prefix them at execution time. Also, remove the "./" of executable's name, when called like ./pyppd-ppdfile. * setup.py, pyppd/runner.py, pyppd/pyppd-ppdfile.in, pyppd/archiver.py: Now, everything will be in only one file by default. When running pyppd, it'll parse the folder passed as argument and create, by default, pyppd-ppdfile. You can run it with list or cat URI to get the compressed PPDs. * pyppd/runner.py: Refactoring (extract method parse_args()) * pyppd/ppd.py: We don't need to save the ppd_file anymore * pyppd/compressor.py: So, when loading the resulting pickle file, we don't need the PPD class definition. We just load a tuple with two strings. * pyppd/runner.py: Adds command-line argument parsing * pyppd/ppd.py, pyppd/compressor.py: PPD class now saves its PPD file in an attribute * pyppd/lzma_proxy.py: communicate() returns a tuple (stdout, stderr). Returns stdout. * pyppd/compressor.py: Refactoring import lzma_proxy * pyppd/ppd.py, pyppd/compressor.py: The PPD class parses a string with the ppd, looking for its attributes. Now, instead of saving a dictionary with ppd_path => {size: ppd_size, start: ppd_start}, it creates a PPD object for each ppd, with it's attributes, so we can in the future list them as foomatic does. * pyppd/lzma_proxy.py, pyppd/compressor.py: This way, we can fallback to using the xz binary, if there isn't python-lzma available. It also has the advantage of using the fastest, if both are available. I need to do some tests to check which is better. * pyppd/compressor.py: Adding compressor.compress() docstring. * pyppd/compressor.py: Forgot the final period. * pyppd/compressor.py: Removing garbage * pyppd/compressor.py: Minor typo fix v0.1.0 (2010-06-12) * setup.py, pyppd/test/__init__.py, pyppd/runner.py, pyppd/compressor.py, pyppd/__init__.py, bin/pyppd, MANIFEST.in, LICENSE.txt, CHANGES.txt, .gitignore: Initial release.