1) Yes! All __future__ imports are defined by the python language committee to always remain valid, in perpetuity.
https://docs.python.org/3/library/__future__.html
Quote:
Originally Posted by The Python Software Foundation
No feature description will ever be deleted from __future__. Since its introduction in Python 2.1 the following features have found their way into the language using this mechanism:
|
Some __future__ imports are no-ops since as early as python 2.2. They denote that the feature became mandatory in a given version, and thereafter serve as executable documentation that such and such a thing happened, once.
You're doing everything you need to, here.
However, there are other cases where you need simple modernization. For example, in several cases your ScrambleEbook plugin uses the idiom:
Code:
raise MyException, errmsg
This is considered to be "old-style" in python2, and is fully removed in python3. On python3 it returns:
Code:
File "/home/eschwartz/git/calibre/src/calibre/customize/zipplugin.py", line 182, in load_module
import_name), 'exec', dont_inherit=True)
File "calibre_plugins.scrambleebook_plugin.action", line 117
raise SelectedBookError, errmsg
^
SyntaxError: invalid syntax
This will work on both, and is preferred even in python2:
Code:
raise MyException(errmsg)
3) This change will indeed work on both python2 and python3. However, it misses the point of why one would want to use iteritems on python2: it is more efficient and uses less memory to use an iterator.
Code:
# python2
>>> {"one":"two", "three": "four"}.items()
[('three', 'four'), ('one', 'two')] # this is a list
>>> {"one":"two", "three": "four"}.iteritems()
<dictionary-itemiterator object at 0x7f6874c5f940> # this is an iterator
>>> {"one":"two", "three": "four"}.viewitems()
dict_items([('three', 'four'), ('one', 'two')]) # this is an iterator
# python3
{"one":"two", "three": "four"}.items()
dict_items([('one', 'two'), ('three', 'four')]) # this is an iterator
The dictionary-itemiterator and dict_items do not create an extra list, which you then toss away. OTOH that also makes it useless in many other cases, since you cannot count len(foo) or index foo[0], if foo is not even a list. (It is curious to note, that iteritems, an iterator, does not have a len, but dict_items which is the native python3 items() and the backported python2 viewitems, and is a view object, does have a len, but is still not subscriptable as foo[0] either way.)
So you trade inefficiency for portability if you use a simple items().
2) b'' strings are for example what you get if you:
Code:
>>> with open('filename', 'wb') as f:
>>> f.write('Hello World')
TypeError: a bytes-like object is required, not 'str'
Which matters a great deal for people working with files. Also, by default, lxml.etree processing works with byte types.
One example of an article discussing the difference and why it matters, is here:
https://medium.com/@andreacolangelo/...w-27dc02ff2686
To use a practical example, after I updated your plugin to use "raise SelectedBookError(errmsg)", and updated all dict.iteritems() to use from polyglot.builtins import iteritems (requires calibre >= 3.41.0) and iteritems(dict), I still got the following message. But this came from calibre itself.
Code:
Traceback (most recent call last):
File "calibre_plugins.scrambleebook_plugin.action", line 161, in show_dialog
File "calibre_plugins.scrambleebook_plugin.scrambleebook", line 189, in __init__
File "calibre_plugins.scrambleebook_plugin.scrambleebook", line 256, in initialise_new_file
File "calibre_plugins.scrambleebook_plugin.scrambleebook", line 1094, in get_run_check_error
File "/home/eschwartz/git/calibre/src/calibre/ebooks/oeb/polish/check/main.py", line 81, in run_checks
errors += check_opf(container)
File "/home/eschwartz/git/calibre/src/calibre/ebooks/oeb/polish/check/opf.py", line 373, in check_opf
n, c = raw.index('name="'), raw.index('content="')
TypeError: argument should be integer or bytes-like object, not 'str'
Now let us take a look at that line in calibre:
https://github.com/kovidgoyal/calibr...ck/opf.py#L373
raw.index is acting on
Code:
raw = etree.tostring(cover)
etree.tostring returns a bytestring, and calibre then tries to index it using a simple str() type, denoted by
This does not work -- on python2, str and bytes are the same, but on python3, str is of type unicode, which is not comparable to bytes, and the solution is to index it using
This will be fixed by the following commit in calibre (which is currently in my fork, but I will PR it soon):
https://github.com/eli-schwartz/cali...2d483a654b56ef
EDIT: now officially merged:
https://github.com/kovidgoyal/calibr...0543e13ef72ac0
The final fix to make the ScrambleEbook plugin work flawlessly AFAICT is to replace all use of unicode() with unicode_type() as imported from polyglot.builtins once again. Although when you used 'mystring %s' % unicode(len(foo)), you can simply drop the unicode cast. You can directly format the string using an integer returned by len() without first converting it to a unicode string.