From 37771022cec803e861250d376dbcec8e9a7728d6 Mon Sep 17 00:00:00 2001 From: un-pogaz <46523284+un-pogaz@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:14:14 +0100 Subject: [PATCH] uniform string quote (auto-fix) ruff 'Q' --- manual/custom.py | 4 +- manual/plugin_examples/interface_demo/main.py | 2 +- recipes/1843.recipe | 38 +- recipes/20_minutos.recipe | 4 +- recipes/DrawAndCook.recipe | 2 +- recipes/TheMITPressReader.recipe | 6 +- recipes/abc_es.recipe | 4 +- recipes/acrimed.recipe | 4 +- recipes/adventuregamers.recipe | 4 +- recipes/afr.recipe | 2 +- recipes/afrique_21.recipe | 6 +- recipes/al_jazeera.recipe | 4 +- recipes/al_monitor.recipe | 6 +- recipes/albert_mohler.recipe | 4 +- recipes/ald.recipe | 8 +- recipes/alternatives_economiques.recipe | 2 +- recipes/am730.recipe | 2 +- recipes/ambito.recipe | 4 +- recipes/american_thinker.recipe | 2 +- recipes/anandtech.recipe | 2 +- recipes/ancient_egypt.recipe | 2 +- recipes/andhrajyothy_ap.recipe | 2 +- recipes/andhrajyothy_tel.recipe | 2 +- recipes/arcamax.recipe | 18 +- recipes/arret_sur_images.recipe | 12 +- recipes/asahi_shimbun_en.recipe | 148 +- recipes/asianreviewofbooks.recipe | 4 +- recipes/ba_herald.recipe | 4 +- recipes/bangkokpost.recipe | 2 +- recipes/barrons.recipe | 8 +- recipes/bbc.recipe | 32 +- recipes/bbc_brasil.recipe | 12 +- recipes/billorielly.recipe | 2 +- recipes/blesk.recipe | 4 +- recipes/blic.recipe | 4 +- recipes/bloomberg-business-week.recipe | 2 +- recipes/bookforummagazine.recipe | 56 +- recipes/borsen_dk.recipe | 6 +- recipes/boston.com.recipe | 28 +- recipes/boston_globe_print_edition.recipe | 14 +- recipes/brewiarz.recipe | 40 +- recipes/business_insider.recipe | 4 +- recipes/business_standard_print.recipe | 2 +- recipes/business_today.recipe | 2 +- recipes/cacm.recipe | 18 +- recipes/calcalist.recipe | 48 +- recipes/calgary_herald.recipe | 30 +- recipes/capital_gr.recipe | 2 +- recipes/caravan_magazine.recipe | 6 +- recipes/cato.recipe | 4 +- recipes/chr_mon.recipe | 4 +- recipes/chronicle_higher_ed.recipe | 10 +- recipes/cicero.recipe | 30 +- recipes/cincinnati_enquirer.recipe | 2 +- recipes/ciperchile.recipe | 4 +- recipes/clarin.recipe | 4 +- recipes/cnetjapan.recipe | 16 +- recipes/cnetjapan_digital.recipe | 16 +- recipes/cnetjapan_release.recipe | 14 +- recipes/cnetnews.recipe | 2 +- recipes/cnn.recipe | 2 +- recipes/contretemps.recipe | 6 +- recipes/cosmos.recipe | 6 +- recipes/courrierinternational.recipe | 8 +- recipes/cubadebate.recipe | 4 +- recipes/dainik_bhaskar.recipe | 2 +- recipes/danas.recipe | 6 +- recipes/degentenaar.recipe | 4 +- recipes/democracy_journal.recipe | 4 +- recipes/demorgen_be.recipe | 10 +- recipes/denik.cz.recipe | 4 +- recipes/denikn.cz.recipe | 8 +- recipes/deredactie.recipe | 10 +- recipes/dilema.recipe | 4 +- recipes/distrowatch_weekly.recipe | 30 +- recipes/dnevnik_cro.recipe | 6 +- recipes/donga.recipe | 40 +- recipes/dr_dk.recipe | 12 +- recipes/dzieje_pl.recipe | 16 +- recipes/dziennik_pl.recipe | 2 +- recipes/dziennik_polski.recipe | 2 +- recipes/economist.recipe | 48 +- recipes/economist_espresso.recipe | 10 +- recipes/economist_free.recipe | 48 +- recipes/economist_news.recipe | 14 +- recipes/economist_search.recipe | 8 +- recipes/economist_world_ahead.recipe | 26 +- recipes/edmonton_journal.recipe | 30 +- recipes/el_colombiano.recipe | 4 +- recipes/el_cultural.recipe | 4 +- recipes/el_diplo.recipe | 94 +- recipes/el_pais.recipe | 4 +- recipes/el_pais_babelia.recipe | 2 +- recipes/elcohetealaluna.recipe | 4 +- recipes/elcronista-arg.recipe | 4 +- recipes/elektroda_pl.recipe | 2 +- recipes/elmundo.recipe | 12 +- recipes/elperiodico_spanish.recipe | 2 +- recipes/en_globes_co_il.recipe | 28 +- recipes/endgadget.recipe | 4 +- recipes/equestria_daily.recipe | 22 +- recipes/expansion_spanish.recipe | 10 +- recipes/fastcompany.recipe | 4 +- recipes/faz_net.recipe | 22 +- recipes/financial_times.recipe | 2 +- recipes/financialsense.recipe | 4 +- recipes/first_things.recipe | 2 +- recipes/flickr.recipe | 4 +- recipes/flickr_es.recipe | 4 +- recipes/fokus.recipe | 6 +- recipes/folha.recipe | 4 +- recipes/folhadesaopaulo_sub.recipe | 6 +- recipes/foreign_policy.recipe | 2 +- recipes/foreignaffairs.recipe | 22 +- recipes/foxnews.recipe | 4 +- recipes/free_inquiry.recipe | 4 +- recipes/frontline.recipe | 2 +- recipes/galaxys_edge.recipe | 12 +- recipes/gazeta-prawna-calibre-v1.recipe | 10 +- recipes/globes_co_il.recipe | 34 +- recipes/go_comics.recipe | 46 +- recipes/google_news.recipe | 2 +- recipes/gosc_full.recipe | 2 +- recipes/granta.recipe | 6 +- recipes/grantland.recipe | 4 +- recipes/greensboro_news_and_record.recipe | 2 +- recipes/guardian.recipe | 8 +- recipes/haaretz_en.recipe | 4 +- recipes/hankyoreh21.recipe | 2 +- recipes/harpers.recipe | 6 +- recipes/hbr.recipe | 128 +- recipes/heise.recipe | 2 +- recipes/heise_ct.recipe | 2 +- recipes/heise_ix.recipe | 2 +- recipes/hindu.recipe | 4 +- recipes/hindustan_times_print.recipe | 2 +- recipes/history_today.recipe | 8 +- recipes/hoy.recipe | 2 +- recipes/hurriyet.recipe | 4 +- recipes/idnes.recipe | 2 +- recipes/ieee_spectrum_mag.recipe | 14 +- recipes/il_messaggero.recipe | 6 +- recipes/il_post.recipe | 44 +- recipes/ilsole24ore.recipe | 4 +- recipes/inc.recipe | 2 +- recipes/independent_australia.recipe | 2 +- recipes/india_today.recipe | 2 +- recipes/indian_express.recipe | 6 +- recipes/ing_dk.recipe | 6 +- recipes/instapaper.recipe | 2 +- recipes/internazionale.recipe | 6 +- recipes/iol_za.recipe | 4 +- recipes/iprofesional.recipe | 4 +- recipes/jacobinmag.recipe | 4 +- recipes/japan_times.recipe | 66 +- recipes/javalobby.recipe | 6 +- recipes/jijinews.recipe | 4 +- recipes/kirkusreviews.recipe | 118 +- recipes/kopalniawiedzy.recipe | 2 +- recipes/korben.recipe | 2 +- recipes/kudy_z_nudy.recipe | 4 +- recipes/la_jornada.recipe | 6 +- recipes/la_republica.recipe | 8 +- recipes/lalibre_be.recipe | 2 +- recipes/lanacion.recipe | 6 +- recipes/lapoliticaonline_ar.recipe | 6 +- recipes/laprensa.recipe | 52 +- recipes/le_canard_enchaine.recipe | 8 +- recipes/le_gorafi.recipe | 4 +- recipes/le_monde_diplomatique_fr.recipe | 4 +- recipes/le_monde_sub_paper.recipe | 54 +- recipes/le_peuple_breton.recipe | 2 +- recipes/leggo_it.recipe | 6 +- recipes/lemonde_dip.recipe | 4 +- recipes/lepoint.recipe | 2 +- recipes/lexpress.recipe | 4 +- recipes/liberation.recipe | 4 +- recipes/libertad_digital.recipe | 4 +- recipes/livemint.recipe | 4 +- recipes/livescience.recipe | 4 +- recipes/lwn_free.recipe | 8 +- recipes/lwn_weekly.recipe | 4 +- recipes/mainichi.recipe | 14 +- recipes/mainichi_en.recipe | 20 +- recipes/mainichi_science_news.recipe | 4 +- recipes/marca.recipe | 4 +- recipes/marctv.recipe | 2 +- recipes/mediaindonesia.recipe | 2 +- recipes/mediapart.recipe | 2 +- recipes/merco_press.recipe | 2 +- recipes/mit_technology_review.recipe | 10 +- recipes/mmc_rtv.recipe | 2 +- recipes/modoros.recipe | 2 +- recipes/montreal_gazette.recipe | 30 +- recipes/nacional_cro.recipe | 6 +- recipes/natgeo.recipe | 4 +- recipes/natgeo_kids.recipe | 4 +- recipes/natgeo_traveller.recipe | 4 +- recipes/natgeohis.recipe | 4 +- recipes/natgeomag.recipe | 4 +- recipes/nature.recipe | 16 +- recipes/nautilus.recipe | 8 +- recipes/new_scientist.recipe | 2 +- recipes/new_scientist_mag.recipe | 2 +- recipes/new_statesman.recipe | 4 +- recipes/new_yorker.recipe | 8 +- recipes/newrepublicmag.recipe | 178 +-- recipes/news24.recipe | 6 +- recipes/news_busters.recipe | 2 +- recipes/newsweek_polska.recipe | 12 +- recipes/nezavisne_novine.recipe | 4 +- recipes/nikkei_news.recipe | 34 +- recipes/nikkeiasia.recipe | 4 +- recipes/njuz_net.recipe | 4 +- recipes/novilist_novine_hr.recipe | 4 +- recipes/novosti.recipe | 4 +- recipes/nrc.nl.recipe | 36 +- recipes/nrc_next.recipe | 2 +- recipes/nspm.recipe | 4 +- recipes/nspm_int.recipe | 4 +- recipes/nyt_magazine.recipe | 4 +- recipes/nyt_tmag.recipe | 4 +- recipes/nytfeeds.recipe | 4 +- recipes/nytimes.recipe | 4 +- recipes/nytimes_sports.recipe | 4 +- recipes/nytimes_sub.recipe | 4 +- recipes/nytimes_tech.recipe | 4 +- recipes/nytimesbook.recipe | 4 +- recipes/observatorul_cultural.recipe | 2 +- recipes/observer_gb.recipe | 14 +- recipes/oc_register.recipe | 24 +- recipes/omgubuntu.recipe | 4 +- recipes/orient_21.recipe | 6 +- recipes/ottawa_citizen.recipe | 30 +- recipes/outlook_india.recipe | 2 +- recipes/pagina12.recipe | 6 +- recipes/parool.recipe | 20 +- recipes/pecat.recipe | 4 +- recipes/people_daily.recipe | 8 +- recipes/pescanik.recipe | 4 +- recipes/politiko_dk.recipe | 8 +- recipes/portafolio.recipe | 4 +- recipes/pravda_por.recipe | 4 +- recipes/presse_portal.recipe | 2 +- recipes/private_eye.recipe | 20 +- recipes/pro_physik.recipe | 2 +- recipes/prospectmaguk_free.recipe | 96 +- recipes/psych.recipe | 8 +- recipes/quanta_magazine.recipe | 12 +- recipes/queueacmorg.recipe | 8 +- recipes/readitlater.recipe | 20 +- recipes/real_clear.recipe | 86 +- recipes/regina_leader_post.recipe | 20 +- recipes/respekt_magazine.recipe | 40 +- recipes/reuters.recipe | 4 +- recipes/revista22.recipe | 2 +- recipes/revista_veintitres.recipe | 6 +- recipes/rts.recipe | 4 +- recipes/russiafeed.recipe | 4 +- recipes/rzeczpospolita.recipe | 6 +- recipes/saskatoon_star_phoenix.recipe | 20 +- recipes/science_news.recipe | 16 +- recipes/scientific_american.recipe | 76 +- recipes/scmp.recipe | 52 +- recipes/scprint.recipe | 8 +- recipes/screen_rant.recipe | 2 +- recipes/seminar_magazine.recipe | 2 +- recipes/sign_of_the_times.recipe | 4 +- recipes/singtaohk.recipe | 2 +- recipes/skeptical_enquirer.recipe | 4 +- recipes/smh.recipe | 64 +- recipes/sol_haber.recipe | 2 +- recipes/star_gazetesi.recipe | 56 +- recipes/strange_horizons.recipe | 4 +- recipes/taz_rss.recipe | 2 +- recipes/telepolis.recipe | 2 +- recipes/thairath.recipe | 2 +- recipes/the_diplomat.recipe | 4 +- recipes/the_federalist.recipe | 4 +- recipes/the_nation.recipe | 6 +- recipes/the_philippine_daily_inquirer.recipe | 2 +- recipes/the_saturday_paper.recipe | 4 +- recipes/the_week_magazine_free.recipe | 2 +- recipes/the_week_uk.recipe | 2 +- recipes/theecocolapse.recipe | 4 +- recipes/theeconomictimes_india.recipe | 2 +- ...heeconomictimes_india_print_edition.recipe | 6 +- recipes/thenewcriterion.recipe | 6 +- recipes/theoldie.recipe | 34 +- recipes/tijd.recipe | 8 +- recipes/toi.recipe | 4 +- recipes/toiprint.recipe | 2 +- recipes/tyzden.recipe | 4 +- recipes/ugeskriftet.recipe | 4 +- recipes/uncrate.recipe | 4 +- recipes/unian_net_en.recipe | 2 +- recipes/vancouver_province.recipe | 30 +- recipes/vancouver_sun.recipe | 30 +- recipes/variety.recipe | 4 +- recipes/vecernji_list.recipe | 6 +- recipes/vic_times.recipe | 24 +- recipes/villagevoice.recipe | 10 +- recipes/volksrant.recipe | 14 +- recipes/vreme.recipe | 4 +- recipes/windows_star.recipe | 4 +- recipes/windsor_star.recipe | 20 +- recipes/wired.recipe | 6 +- recipes/wired_daily.recipe | 4 +- recipes/words_without_borders.recipe | 4 +- recipes/wsj.recipe | 2 +- recipes/wsj_free.recipe | 4 +- recipes/wsj_news.recipe | 2 +- recipes/yomiuri_world.recipe | 4 +- recipes/zaobao.recipe | 2 +- recipes/zdnet.fr.recipe | 2 +- recipes/zeitde_sub.recipe | 10 +- recipes/zerodeux.recipe | 2 +- recipes/zycie_warszawy.recipe | 2 +- resources/default_tweaks.py | 2 +- ruff-strict-pep8.toml | 8 +- setup/build.py | 10 +- setup/hosting.py | 2 +- setup/install.py | 2 +- setup/installers.py | 2 +- setup/plugins_mirror.py | 2 +- setup/publish.py | 4 +- setup/upload.py | 4 +- setup/vcvars.py | 34 +- setup/wincross.py | 16 +- src/calibre/__init__.py | 8 +- src/calibre/constants.py | 2 +- src/calibre/customize/__init__.py | 8 +- src/calibre/customize/builtins.py | 6 +- src/calibre/customize/zipplugin.py | 4 +- src/calibre/db/backend.py | 6 +- src/calibre/db/cache.py | 2 +- src/calibre/db/cli/cmd_catalog.py | 14 +- src/calibre/db/cli/cmd_check_library.py | 12 +- src/calibre/db/cli/cmd_list.py | 2 +- src/calibre/db/cli/cmd_list_categories.py | 4 +- src/calibre/db/cli/tests.py | 16 +- src/calibre/db/fts/connect.py | 2 +- src/calibre/db/lazy.py | 4 +- src/calibre/db/locking.py | 6 +- src/calibre/db/notes/connect.py | 2 +- src/calibre/db/schema_upgrades.py | 4 +- src/calibre/db/tests/fts.py | 40 +- src/calibre/db/tests/legacy.py | 4 +- src/calibre/db/tests/locking.py | 2 +- src/calibre/db/tests/reading.py | 8 +- src/calibre/db/tests/writing.py | 14 +- src/calibre/debug.py | 2 +- src/calibre/devices/__init__.py | 2 +- src/calibre/devices/android/driver.py | 4 +- src/calibre/devices/cli.py | 152 +- src/calibre/devices/cybook/t2b.py | 4 +- src/calibre/devices/cybook/t4b.py | 2 +- src/calibre/devices/eb600/driver.py | 2 +- src/calibre/devices/errors.py | 54 +- src/calibre/devices/interface.py | 14 +- src/calibre/devices/jetbook/driver.py | 4 +- src/calibre/devices/kindle/apnx.py | 8 +- .../generators/accurate_page_generator.py | 6 +- .../generators/exact_page_generator.py | 6 +- .../generators/fast_page_generator.py | 4 +- .../generators/pagebreak_page_generator.py | 4 +- .../apnx_page_generator/i_page_generator.py | 4 +- .../kindle/apnx_page_generator/page_group.py | 6 +- .../apnx_page_generator/page_number_type.py | 6 +- .../kindle/apnx_page_generator/pages.py | 2 +- src/calibre/devices/kindle/bookmark.py | 14 +- src/calibre/devices/kindle/driver.py | 8 +- src/calibre/devices/kobo/bookmark.py | 14 +- src/calibre/devices/kobo/books.py | 60 +- src/calibre/devices/kobo/driver.py | 496 +++--- src/calibre/devices/kobo/kobotouch_config.py | 92 +- src/calibre/devices/mtp/unix/driver.py | 2 +- src/calibre/devices/mtp/windows/driver.py | 2 +- src/calibre/devices/paladin/driver.py | 32 +- src/calibre/devices/prs505/sony_cache.py | 18 +- src/calibre/devices/prst1/driver.py | 34 +- .../devices/smart_device_app/driver.py | 34 +- src/calibre/devices/usbms/device.py | 2 +- src/calibre/devices/usbms/driver.py | 2 +- src/calibre/devices/usbms/hal.py | 10 +- src/calibre/devices/user_defined/driver.py | 2 +- src/calibre/devices/utils.py | 8 +- src/calibre/devices/winusb.py | 22 +- src/calibre/ebooks/chardet.py | 2 +- src/calibre/ebooks/chm/metadata.py | 6 +- src/calibre/ebooks/chm/reader.py | 8 +- src/calibre/ebooks/conversion/cli.py | 4 +- .../ebooks/conversion/plugins/chm_input.py | 2 +- .../ebooks/conversion/plugins/comic_input.py | 6 +- .../ebooks/conversion/plugins/djvu_input.py | 2 +- .../ebooks/conversion/plugins/epub_input.py | 2 +- .../ebooks/conversion/plugins/epub_output.py | 2 +- .../ebooks/conversion/plugins/fb2_input.py | 6 +- .../ebooks/conversion/plugins/html_output.py | 2 +- .../ebooks/conversion/plugins/htmlz_output.py | 2 +- .../ebooks/conversion/plugins/lrf_output.py | 2 +- .../ebooks/conversion/plugins/mobi_output.py | 4 +- .../ebooks/conversion/plugins/snb_input.py | 10 +- .../ebooks/conversion/plugins/snb_output.py | 60 +- .../ebooks/conversion/plugins/txt_input.py | 6 +- .../ebooks/conversion/plugins/txt_output.py | 6 +- src/calibre/ebooks/conversion/plumber.py | 2 +- src/calibre/ebooks/conversion/preprocess.py | 26 +- src/calibre/ebooks/conversion/utils.py | 164 +- src/calibre/ebooks/covers.py | 2 +- src/calibre/ebooks/djvu/djvubzzdec.py | 18 +- src/calibre/ebooks/docx/index.py | 6 +- src/calibre/ebooks/docx/to_html.py | 2 +- src/calibre/ebooks/docx/writer/container.py | 36 +- src/calibre/ebooks/docx/writer/fonts.py | 2 +- src/calibre/ebooks/docx/writer/images.py | 10 +- src/calibre/ebooks/docx/writer/links.py | 2 +- src/calibre/ebooks/docx/writer/lists.py | 2 +- src/calibre/ebooks/docx/writer/tables.py | 2 +- src/calibre/ebooks/epub/pages.py | 2 +- src/calibre/ebooks/html_transform_rules.py | 4 +- src/calibre/ebooks/hyphenate.py | 20 +- src/calibre/ebooks/lit/maps/__init__.py | 4 +- src/calibre/ebooks/lit/maps/html.py | 1420 ++++++++--------- src/calibre/ebooks/lit/maps/opf.py | 106 +- src/calibre/ebooks/lit/mssha1.py | 36 +- src/calibre/ebooks/lit/reader.py | 76 +- src/calibre/ebooks/lit/writer.py | 54 +- src/calibre/ebooks/lrf/__init__.py | 10 +- src/calibre/ebooks/lrf/html/__init__.py | 8 +- src/calibre/ebooks/lrf/html/convert_from.py | 118 +- src/calibre/ebooks/lrf/html/table.py | 2 +- src/calibre/ebooks/lrf/input.py | 6 +- src/calibre/ebooks/lrf/lrfparser.py | 6 +- src/calibre/ebooks/lrf/meta.py | 242 +-- src/calibre/ebooks/lrf/objects.py | 64 +- src/calibre/ebooks/lrf/pylrs/__init__.py | 4 +- src/calibre/ebooks/lrf/pylrs/elements.py | 12 +- src/calibre/ebooks/lrf/pylrs/pylrf.py | 164 +- src/calibre/ebooks/lrf/pylrs/pylrfopt.py | 6 +- src/calibre/ebooks/lrf/pylrs/pylrs.py | 642 ++++---- src/calibre/ebooks/lrf/tags.py | 80 +- src/calibre/ebooks/metadata/__init__.py | 6 +- src/calibre/ebooks/metadata/book/base.py | 4 +- .../ebooks/metadata/book/json_codec.py | 6 +- src/calibre/ebooks/metadata/book/render.py | 2 +- src/calibre/ebooks/metadata/epub.py | 22 +- src/calibre/ebooks/metadata/ereader.py | 4 +- src/calibre/ebooks/metadata/fb2.py | 2 +- src/calibre/ebooks/metadata/imp.py | 6 +- src/calibre/ebooks/metadata/kdl.py | 2 +- src/calibre/ebooks/metadata/kfx.py | 14 +- src/calibre/ebooks/metadata/mobi.py | 28 +- src/calibre/ebooks/metadata/odt.py | 2 +- src/calibre/ebooks/metadata/opf2.py | 12 +- src/calibre/ebooks/metadata/opf3.py | 2 +- src/calibre/ebooks/metadata/pdb.py | 4 +- src/calibre/ebooks/metadata/pml.py | 2 +- src/calibre/ebooks/metadata/rb.py | 8 +- src/calibre/ebooks/metadata/rtf.py | 14 +- src/calibre/ebooks/metadata/snb.py | 2 +- src/calibre/ebooks/metadata/sources/amazon.py | 10 +- src/calibre/ebooks/metadata/sources/base.py | 4 +- .../ebooks/metadata/sources/edelweiss.py | 6 +- src/calibre/ebooks/metadata/sources/test.py | 12 +- src/calibre/ebooks/metadata/toc.py | 4 +- src/calibre/ebooks/metadata/topaz.py | 24 +- src/calibre/ebooks/mobi/debug/headers.py | 2 +- src/calibre/ebooks/mobi/langcodes.py | 194 +-- src/calibre/ebooks/mobi/mobiml.py | 6 +- src/calibre/ebooks/mobi/reader/index.py | 4 +- src/calibre/ebooks/mobi/reader/markup.py | 18 +- src/calibre/ebooks/mobi/reader/mobi6.py | 4 +- src/calibre/ebooks/mobi/reader/mobi8.py | 6 +- src/calibre/ebooks/mobi/reader/ncx.py | 4 +- src/calibre/ebooks/mobi/writer2/serializer.py | 2 +- src/calibre/ebooks/mobi/writer8/exth.py | 2 +- src/calibre/ebooks/mobi/writer8/main.py | 2 +- src/calibre/ebooks/odt/input.py | 6 +- src/calibre/ebooks/oeb/base.py | 100 +- src/calibre/ebooks/oeb/polish/check/links.py | 4 +- src/calibre/ebooks/oeb/polish/container.py | 8 +- src/calibre/ebooks/oeb/polish/cover.py | 2 +- src/calibre/ebooks/oeb/polish/create.py | 2 +- .../ebooks/oeb/polish/tests/parsing.py | 2 +- src/calibre/ebooks/oeb/polish/toc.py | 4 +- src/calibre/ebooks/oeb/reader.py | 24 +- src/calibre/ebooks/oeb/stylizer.py | 8 +- .../ebooks/oeb/transforms/filenames.py | 2 +- src/calibre/ebooks/oeb/transforms/flatcss.py | 20 +- src/calibre/ebooks/oeb/transforms/htmltoc.py | 8 +- src/calibre/ebooks/oeb/transforms/jacket.py | 6 +- .../ebooks/oeb/transforms/manglecase.py | 4 +- .../ebooks/oeb/transforms/rasterize.py | 4 +- .../ebooks/oeb/transforms/structure.py | 2 +- src/calibre/ebooks/oeb/writer.py | 16 +- src/calibre/ebooks/pdb/haodoo/reader.py | 52 +- src/calibre/ebooks/pdf/html_writer.py | 8 +- src/calibre/ebooks/pdf/pdftohtml.py | 4 +- src/calibre/ebooks/pdf/render/common.py | 2 +- src/calibre/ebooks/pdf/render/graphics.py | 272 ++-- src/calibre/ebooks/readability/cleaners.py | 14 +- src/calibre/ebooks/readability/debug.py | 4 +- src/calibre/ebooks/readability/readability.py | 82 +- src/calibre/ebooks/render_html.py | 2 +- src/calibre/ebooks/rtf/preprocess.py | 18 +- src/calibre/ebooks/rtf2xml/ParseRtf.py | 34 +- src/calibre/ebooks/rtf2xml/add_brackets.py | 42 +- src/calibre/ebooks/rtf2xml/body_styles.py | 14 +- src/calibre/ebooks/rtf2xml/border_parse.py | 8 +- src/calibre/ebooks/rtf2xml/char_set.py | 4 +- src/calibre/ebooks/rtf2xml/check_brackets.py | 6 +- src/calibre/ebooks/rtf2xml/colors.py | 40 +- src/calibre/ebooks/rtf2xml/combine_borders.py | 6 +- src/calibre/ebooks/rtf2xml/configure_txt.py | 4 +- src/calibre/ebooks/rtf2xml/convert_to_tags.py | 46 +- src/calibre/ebooks/rtf2xml/copy.py | 18 +- .../ebooks/rtf2xml/default_encoding.py | 4 +- src/calibre/ebooks/rtf2xml/delete_info.py | 28 +- src/calibre/ebooks/rtf2xml/field_strings.py | 100 +- src/calibre/ebooks/rtf2xml/fields_large.py | 38 +- src/calibre/ebooks/rtf2xml/fields_small.py | 46 +- src/calibre/ebooks/rtf2xml/fonts.py | 30 +- src/calibre/ebooks/rtf2xml/footnote.py | 50 +- src/calibre/ebooks/rtf2xml/get_char_map.py | 4 +- src/calibre/ebooks/rtf2xml/get_options.py | 24 +- src/calibre/ebooks/rtf2xml/group_borders.py | 26 +- src/calibre/ebooks/rtf2xml/group_styles.py | 26 +- src/calibre/ebooks/rtf2xml/header.py | 52 +- .../ebooks/rtf2xml/headings_to_sections.py | 24 +- src/calibre/ebooks/rtf2xml/hex_2_utf8.py | 74 +- src/calibre/ebooks/rtf2xml/info.py | 42 +- src/calibre/ebooks/rtf2xml/inline.py | 50 +- src/calibre/ebooks/rtf2xml/line_endings.py | 4 +- src/calibre/ebooks/rtf2xml/list_numbers.py | 40 +- src/calibre/ebooks/rtf2xml/list_table.py | 64 +- src/calibre/ebooks/rtf2xml/make_lists.py | 44 +- src/calibre/ebooks/rtf2xml/old_rtf.py | 8 +- src/calibre/ebooks/rtf2xml/options_trem.py | 32 +- src/calibre/ebooks/rtf2xml/output.py | 20 +- src/calibre/ebooks/rtf2xml/override_table.py | 32 +- src/calibre/ebooks/rtf2xml/paragraph_def.py | 74 +- src/calibre/ebooks/rtf2xml/paragraphs.py | 34 +- src/calibre/ebooks/rtf2xml/pict.py | 40 +- src/calibre/ebooks/rtf2xml/preamble_div.py | 78 +- src/calibre/ebooks/rtf2xml/preamble_rest.py | 22 +- src/calibre/ebooks/rtf2xml/process_tokens.py | 44 +- .../ebooks/rtf2xml/replace_illegals.py | 10 +- src/calibre/ebooks/rtf2xml/sections.py | 86 +- src/calibre/ebooks/rtf2xml/styles.py | 70 +- src/calibre/ebooks/rtf2xml/table.py | 54 +- src/calibre/ebooks/rtf2xml/table_info.py | 14 +- src/calibre/ebooks/rtf2xml/tokenize.py | 56 +- src/calibre/ebooks/snb/snbfile.py | 62 +- src/calibre/ebooks/snb/snbml.py | 42 +- src/calibre/ebooks/textile/functions.py | 110 +- src/calibre/ebooks/textile/unsmarten.py | 2 +- src/calibre/ebooks/txt/processor.py | 2 +- src/calibre/ebooks/unihandecode/__init__.py | 4 +- src/calibre/ebooks/unihandecode/jadecoder.py | 16 +- .../ebooks/unihandecode/unicodepoints.py | 102 +- src/calibre/ebooks/unihandecode/unidecoder.py | 4 +- src/calibre/gui2/__init__.py | 8 +- src/calibre/gui2/actions/all_actions.py | 6 +- src/calibre/gui2/actions/catalog.py | 4 +- src/calibre/gui2/actions/delete.py | 2 +- src/calibre/gui2/actions/device.py | 2 +- src/calibre/gui2/actions/layout_actions.py | 4 +- src/calibre/gui2/actions/open.py | 2 +- src/calibre/gui2/actions/save_to_disk.py | 2 +- src/calibre/gui2/bars.py | 8 +- src/calibre/gui2/book_details.py | 6 +- src/calibre/gui2/catalog/catalog_csv_xml.py | 2 +- src/calibre/gui2/catalog/catalog_epub_mobi.py | 94 +- src/calibre/gui2/comments_editor.py | 6 +- src/calibre/gui2/convert/__init__.py | 6 +- src/calibre/gui2/convert/gui_conversion.py | 2 +- src/calibre/gui2/convert/metadata.py | 8 +- src/calibre/gui2/convert/single.py | 32 +- src/calibre/gui2/custom_column_widgets.py | 4 +- src/calibre/gui2/device.py | 2 +- .../device_drivers/tabbed_device_config.py | 40 +- src/calibre/gui2/dialogs/add_from_isbn.py | 22 +- src/calibre/gui2/dialogs/catalog.py | 8 +- src/calibre/gui2/dialogs/choose_format.py | 2 +- src/calibre/gui2/dialogs/comments_dialog.py | 4 +- src/calibre/gui2/dialogs/confirm_delete.py | 10 +- src/calibre/gui2/dialogs/custom_recipes.py | 20 +- .../gui2/dialogs/data_files_manager.py | 2 +- .../gui2/dialogs/edit_authors_dialog.py | 4 +- src/calibre/gui2/dialogs/match_books.py | 2 +- src/calibre/gui2/dialogs/message_box.py | 12 +- src/calibre/gui2/dialogs/metadata_bulk.py | 48 +- src/calibre/gui2/dialogs/multisort.py | 2 +- src/calibre/gui2/dialogs/plugin_updater.py | 6 +- src/calibre/gui2/dialogs/quickview.py | 2 +- src/calibre/gui2/dialogs/restore_library.py | 2 +- src/calibre/gui2/dialogs/scheduler.py | 58 +- src/calibre/gui2/dialogs/search.py | 38 +- src/calibre/gui2/dialogs/tag_list_editor.py | 6 +- src/calibre/gui2/dialogs/template_dialog.py | 134 +- src/calibre/gui2/font_family_chooser.py | 2 +- src/calibre/gui2/icon_theme.py | 2 +- src/calibre/gui2/init.py | 2 +- src/calibre/gui2/job_indicator.py | 2 +- src/calibre/gui2/layout.py | 10 +- src/calibre/gui2/library/annotations.py | 2 +- src/calibre/gui2/library/delegates.py | 2 +- src/calibre/gui2/library/models.py | 18 +- src/calibre/gui2/library/views.py | 2 +- src/calibre/gui2/lrf_renderer/text.py | 10 +- src/calibre/gui2/main.py | 2 +- src/calibre/gui2/markdown_editor.py | 4 +- .../gui2/markdown_syntax_highlighter.py | 96 +- src/calibre/gui2/metadata/basic_widgets.py | 18 +- src/calibre/gui2/metadata/single.py | 12 +- src/calibre/gui2/metadata/single_download.py | 2 +- src/calibre/gui2/notify.py | 10 +- src/calibre/gui2/preferences/columns.py | 2 +- .../gui2/preferences/create_custom_column.py | 74 +- src/calibre/gui2/preferences/plugins.py | 2 +- src/calibre/gui2/preferences/server.py | 4 +- src/calibre/gui2/preferences/tweaks.py | 32 +- src/calibre/gui2/proceed.py | 4 +- src/calibre/gui2/qt_file_dialogs.py | 6 +- src/calibre/gui2/search_restriction_mixin.py | 2 +- src/calibre/gui2/splash_screen.py | 2 +- src/calibre/gui2/store/search/search.py | 4 +- .../gui2/store/stores/amazon_es_plugin.py | 2 +- .../gui2/store/stores/amazon_it_plugin.py | 2 +- src/calibre/gui2/store/stores/bn_plugin.py | 2 +- .../gui2/store/stores/chitanka_plugin.py | 2 +- .../gui2/store/stores/ebooks_com_plugin.py | 2 +- .../store/stores/ebookshoppe_uk_plugin.py | 2 +- .../gui2/store/stores/gutenberg_plugin.py | 2 +- .../gui2/store/stores/litres_plugin.py | 2 +- src/calibre/gui2/tag_browser/model.py | 6 +- src/calibre/gui2/tag_browser/ui.py | 2 +- src/calibre/gui2/tag_browser/view.py | 6 +- src/calibre/gui2/toc/location.py | 4 +- src/calibre/gui2/tools.py | 2 +- src/calibre/gui2/tts/types.py | 2 +- src/calibre/gui2/tweak_book/char_select.py | 2 +- src/calibre/gui2/tweak_book/diff/main.py | 2 +- src/calibre/gui2/tweak_book/diff/view.py | 2 +- .../gui2/tweak_book/editor/syntax/css.py | 2 +- .../tweak_book/editor/syntax/javascript.py | 4 +- .../editor/syntax/pygments_highlighter.py | 4 +- src/calibre/gui2/tweak_book/preferences.py | 2 +- src/calibre/gui2/tweak_book/preview.py | 2 +- src/calibre/gui2/viewer/lookup.py | 2 +- src/calibre/gui2/viewer/ui.py | 8 +- src/calibre/gui2/viewer/web_view.py | 6 +- src/calibre/gui2/widgets.py | 104 +- src/calibre/gui2/win_file_dialogs.py | 2 +- src/calibre/gui2/wizard/__init__.py | 6 +- src/calibre/gui2/wizard/send_email.py | 6 +- src/calibre/library/catalogs/bibtex.py | 42 +- src/calibre/library/catalogs/csv_xml.py | 10 +- src/calibre/library/catalogs/epub_mobi.py | 70 +- .../library/catalogs/epub_mobi_builder.py | 924 +++++------ src/calibre/library/catalogs/utils.py | 70 +- src/calibre/library/coloring.py | 24 +- src/calibre/library/database2.py | 10 +- src/calibre/library/prefs.py | 4 +- src/calibre/libunzip.py | 4 +- src/calibre/linux.py | 20 +- src/calibre/ptempfile.py | 16 +- src/calibre/rpdb.py | 8 +- src/calibre/scraper/test_fetch_backend.py | 2 +- src/calibre/spell/import_from.py | 4 +- src/calibre/srv/ajax.py | 4 +- src/calibre/srv/auth.py | 10 +- src/calibre/srv/http_request.py | 28 +- src/calibre/srv/http_response.py | 26 +- src/calibre/srv/legacy.py | 8 +- src/calibre/srv/loop.py | 14 +- src/calibre/srv/metadata.py | 2 +- src/calibre/srv/opds.py | 12 +- src/calibre/srv/pre_activated.py | 8 +- src/calibre/srv/routes.py | 2 +- src/calibre/srv/standalone.py | 2 +- src/calibre/srv/tests/content.py | 8 +- src/calibre/srv/tests/http.py | 2 +- src/calibre/srv/tests/web_sockets.py | 6 +- src/calibre/srv/utils.py | 28 +- src/calibre/srv/web_socket.py | 8 +- src/calibre/test_build.py | 6 +- src/calibre/translations/msgfmt.py | 22 +- src/calibre/utils/bibtex.py | 24 +- src/calibre/utils/certgen.py | 2 +- src/calibre/utils/cleantext.py | 6 +- src/calibre/utils/config.py | 22 +- src/calibre/utils/config_base.py | 4 +- src/calibre/utils/ffml_processor.py | 28 +- src/calibre/utils/fonts/sfnt/__init__.py | 4 +- src/calibre/utils/fonts/sfnt/cff/constants.py | 186 +-- src/calibre/utils/fonts/sfnt/cff/dict_data.py | 46 +- src/calibre/utils/fonts/sfnt/cff/table.py | 2 +- src/calibre/utils/fonts/sfnt/loca.py | 2 +- src/calibre/utils/formatter.py | 62 +- src/calibre/utils/formatter_functions.py | 16 +- src/calibre/utils/icu_test.py | 6 +- src/calibre/utils/img.py | 32 +- src/calibre/utils/imghdr.py | 26 +- src/calibre/utils/inotify.py | 10 +- src/calibre/utils/iphlpapi.py | 8 +- src/calibre/utils/ipython.py | 10 +- src/calibre/utils/iso8601.py | 2 +- src/calibre/utils/linux_trash.py | 12 +- src/calibre/utils/matcher.py | 2 +- src/calibre/utils/mem.py | 8 +- src/calibre/utils/mreplace.py | 4 +- src/calibre/utils/network.py | 4 +- src/calibre/utils/opensearch/description.py | 2 +- src/calibre/utils/podofo/__init__.py | 2 +- src/calibre/utils/rapydscript.py | 2 +- src/calibre/utils/search_query_parser.py | 2 +- src/calibre/utils/shm.py | 10 +- src/calibre/utils/smartypants.py | 230 +-- src/calibre/utils/smtp.py | 2 +- src/calibre/utils/smtplib.py | 180 +-- src/calibre/utils/terminal.py | 24 +- src/calibre/utils/text2int.py | 26 +- src/calibre/utils/threadpool.py | 72 +- src/calibre/utils/titlecase.py | 14 +- src/calibre/utils/unicode_names.py | 2 +- src/calibre/utils/winreg/dde.py | 2 +- src/calibre/utils/winreg/lib.py | 2 +- src/calibre/utils/wordcount.py | 18 +- src/calibre/utils/zipfile.py | 278 ++-- src/calibre/web/__init__.py | 6 +- src/calibre/web/feeds/__init__.py | 2 +- src/calibre/web/feeds/news.py | 8 +- src/calibre/web/feeds/templates.py | 20 +- src/odf/attrconverters.py | 62 +- src/odf/easyliststyle.py | 22 +- src/odf/element.py | 142 +- src/odf/grammar.py | 4 +- src/odf/load.py | 8 +- src/odf/namespaces.py | 84 +- src/odf/odf2moinmoin.py | 246 +-- src/odf/odf2xhtml.py | 832 +++++----- src/odf/odfmanifest.py | 12 +- src/odf/office.py | 10 +- src/odf/opendocument.py | 142 +- src/odf/teletype.py | 24 +- src/odf/thumbnail.py | 8 +- src/odf/userfield.py | 36 +- src/qt/__init__.py | 2 +- 750 files changed, 8704 insertions(+), 8698 deletions(-) diff --git a/manual/custom.py b/manual/custom.py index ee8f429cdd..cd588b5c28 100644 --- a/manual/custom.py +++ b/manual/custom.py @@ -240,14 +240,14 @@ def generate_ebook_convert_help(preamble, app): parser, plumber = create_option_parser(['ebook-convert', 'dummyi.'+sorted(pl.file_types)[0], 'dummyo.epub', '-h'], default_log) groups = [(pl.name+ ' Options', '', g.option_list) for g in - parser.option_groups if g.title == "INPUT OPTIONS"] + parser.option_groups if g.title == 'INPUT OPTIONS'] prog = 'ebook-convert-'+(pl.name.lower().replace(' ', '-')) raw += '\n\n' + '\n'.join(render_options(prog, groups, False, True)) for pl in sorted(output_format_plugins(), key=lambda x: x.name): parser, plumber = create_option_parser(['ebook-convert', 'd.epub', 'dummyi.'+pl.file_type, '-h'], default_log) groups = [(pl.name+ ' Options', '', g.option_list) for g in - parser.option_groups if g.title == "OUTPUT OPTIONS"] + parser.option_groups if g.title == 'OUTPUT OPTIONS'] prog = 'ebook-convert-'+(pl.name.lower().replace(' ', '-')) raw += '\n\n' + '\n'.join(render_options(prog, groups, False, True)) diff --git a/manual/plugin_examples/interface_demo/main.py b/manual/plugin_examples/interface_demo/main.py index c3c35f714f..2e6fd25df7 100644 --- a/manual/plugin_examples/interface_demo/main.py +++ b/manual/plugin_examples/interface_demo/main.py @@ -55,7 +55,7 @@ class DemoDialog(QDialog): self.l.addWidget(self.view_button) self.update_metadata_button = QPushButton( - 'Update metadata in a book\'s files', self) + "Update metadata in a book's files", self) self.update_metadata_button.clicked.connect(self.update_metadata) self.l.addWidget(self.update_metadata_button) diff --git a/recipes/1843.recipe b/recipes/1843.recipe index 3361c721ed..81c1437ee4 100644 --- a/recipes/1843.recipe +++ b/recipes/1843.recipe @@ -61,7 +61,7 @@ if use_archive: body = root.xpath('//body')[0] article = E(body, 'article') E(article, 'div', data['flyTitle'] , style='color: red; font-size:small; font-weight:bold;') - E(article, 'h1', data['title'], title=safe_dict(data, "url", "canonical") or '') + E(article, 'h1', data['title'], title=safe_dict(data, 'url', 'canonical') or '') E(article, 'div', data['rubric'], style='font-style: italic; color:#202020;') try: date = data['dateModified'] @@ -157,7 +157,7 @@ class Economist(BasicNewsRecipe): encoding = 'utf-8' masthead_url = 'https://www.livemint.com/lm-img/dev/economist-logo-oneline.png' - __author__ = "Kovid Goyal" + __author__ = 'Kovid Goyal' description = ( 'Published since September 1843 to take part in “a severe contest between intelligence, which presses forward, and ' 'an unworthy, timid ignorance obstructing our progress.”' @@ -170,7 +170,7 @@ class Economist(BasicNewsRecipe): resolve_internal_links = True remove_tags = [ dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent', 'aside', 'footer']), - dict(attrs={'aria-label': "Article Teaser"}), + dict(attrs={'aria-label': 'Article Teaser'}), dict(attrs={ 'class': [ 'dblClkTrk', 'ec-article-info', 'share_inline_header', @@ -224,7 +224,7 @@ class Economist(BasicNewsRecipe): def parse_index(self): # return self.economist_test_article() soup = self.index_to_soup('https://www.economist.com/hidden-content/1843magazine-hub') - script_tag = soup.find("script", id="__NEXT_DATA__") + script_tag = soup.find('script', id='__NEXT_DATA__') if script_tag is None: raise ValueError('No script tag with JSON data found in the weeklyedition archive') data = json.loads(script_tag.string) @@ -247,20 +247,20 @@ class Economist(BasicNewsRecipe): self.description = data['description'] feeds_dict = defaultdict(list) - for part in safe_dict(data, "hasPart", "parts"): + for part in safe_dict(data, 'hasPart', 'parts'): section = part['title'] self.log(section) - for art in safe_dict(part, "hasPart", "parts"): - title = safe_dict(art, "title") - desc = safe_dict(art, "rubric") or '' - sub = safe_dict(art, "flyTitle") or '' + for art in safe_dict(part, 'hasPart', 'parts'): + title = safe_dict(art, 'title') + desc = safe_dict(art, 'rubric') or '' + sub = safe_dict(art, 'flyTitle') or '' if sub and section != sub: desc = sub + ' :: ' + desc pt = PersistentTemporaryFile('.html') pt.write(json.dumps(art).encode('utf-8')) pt.close() url = 'file:///' + pt.name - feeds_dict[section].append({"title": title, "url": url, "description": desc}) + feeds_dict[section].append({'title': title, 'url': url, 'description': desc}) self.log('\t', title, '\n\t\t', desc) return [(section, articles) for section, articles in feeds_dict.items()] @@ -311,26 +311,26 @@ class Economist(BasicNewsRecipe): return ans def economist_parse_index(self, soup): - script_tag = soup.find("script", id="__NEXT_DATA__") + script_tag = soup.find('script', id='__NEXT_DATA__') if script_tag is not None: data = json.loads(script_tag.string) # open('/t/raw.json', 'w').write(json.dumps(data, indent=2, sort_keys=True)) - self.title = safe_dict(data, "props", "pageProps", "content", "headline") + self.title = safe_dict(data, 'props', 'pageProps', 'content', 'headline') # self.cover_url = 'https://mma.prnewswire.com/media/2275620/The_Economist_The_World_Ahead_2024.jpg?w=600' feeds = [] - for coll in safe_dict(data, "props", "pageProps", "content", "collections"): - section = safe_dict(coll, "headline") or '' + for coll in safe_dict(data, 'props', 'pageProps', 'content', 'collections'): + section = safe_dict(coll, 'headline') or '' self.log(section) articles = [] - for part in safe_dict(coll, "hasPart", "parts"): - title = safe_dict(part, "headline") or '' - url = safe_dict(part, "url", "canonical") or '' + for part in safe_dict(coll, 'hasPart', 'parts'): + title = safe_dict(part, 'headline') or '' + url = safe_dict(part, 'url', 'canonical') or '' if not title or not url: continue - desc = safe_dict(part, "description") or '' - sub = safe_dict(part, "subheadline") or '' + desc = safe_dict(part, 'description') or '' + sub = safe_dict(part, 'subheadline') or '' if sub: desc = sub + ' :: ' + desc self.log('\t', title, '\n\t', desc, '\n\t\t', url) diff --git a/recipes/20_minutos.recipe b/recipes/20_minutos.recipe index fd0d2d029f..02e447c633 100644 --- a/recipes/20_minutos.recipe +++ b/recipes/20_minutos.recipe @@ -47,11 +47,11 @@ class AdvancedUserRecipe1294946868(BasicNewsRecipe): dict(name='ol', attrs={'class': ['navigation', ]}), dict(name='span', attrs={'class': ['action']}), dict(name='div', attrs={'class': ['twitter comments-list hidden', 'related-news', 'col', 'photo-gallery', 'photo-gallery side-art-block', 'calendario', 'article-comment', 'postto estirar', 'otras_vinetas estirar', 'kment', 'user-actions']}), dict( name='div', attrs={'id': ['twitter-destacados', 'eco-tabs', 'inner', 'vineta_calendario', 'vinetistas clearfix', 'otras_vinetas estirar', 'MIN1', 'main', 'SUP1', 'INT']}), dict(name='ul', attrs={'class': ['article-user-actions', 'stripped-list']}), dict(name='ul', attrs={'id': ['site-links']}), dict(name='li', attrs={'class': ['puntuacion', 'enviar', 'compartir']}) # noqa: E501 ] - extra_css = """ + extra_css = ''' p{text-align: justify; font-size: 100%} body{ text-align: left; font-size:100% } h3{font-family: sans-serif; font-size:150%; font-weight:bold; text-align: justify; } - """ + ''' preprocess_regexps = [(re.compile( r'', re.DOTALL), lambda m: '')] diff --git a/recipes/DrawAndCook.recipe b/recipes/DrawAndCook.recipe index 95a74b8cfa..7916fe512b 100644 --- a/recipes/DrawAndCook.recipe +++ b/recipes/DrawAndCook.recipe @@ -28,7 +28,7 @@ class DrawAndCook(BasicNewsRecipe): def parse_index(self): feeds = [] for title, url in [ - ("They Draw and Cook", "http://www.theydrawandcook.com/") + ('They Draw and Cook', 'http://www.theydrawandcook.com/') ]: articles = self.make_links(url) if articles: diff --git a/recipes/TheMITPressReader.recipe b/recipes/TheMITPressReader.recipe index 84417ce803..c64226e771 100644 --- a/recipes/TheMITPressReader.recipe +++ b/recipes/TheMITPressReader.recipe @@ -5,11 +5,11 @@ from calibre.web.feeds.news import BasicNewsRecipe class TheMITPressReader(BasicNewsRecipe): - title = "The MIT Press Reader" + title = 'The MIT Press Reader' __author__ = 'yodha8' language = 'en' - description = ("Thought-provoking excerpts, interviews and essays backed by academic rigor written by MIT Press authors." - " This recipe pulls articles from the past 7 days.") + description = ('Thought-provoking excerpts, interviews and essays backed by academic rigor written by MIT Press authors.' + ' This recipe pulls articles from the past 7 days.') oldest_article = 7 max_articles_per_feed = 100 auto_cleanup = True diff --git a/recipes/abc_es.recipe b/recipes/abc_es.recipe index f5b036b359..0f902a0202 100644 --- a/recipes/abc_es.recipe +++ b/recipes/abc_es.recipe @@ -47,13 +47,13 @@ class AdvancedUserRecipe1296604369(BasicNewsRecipe): if d and isinstance(d, str): self.oldest_article = float(d) - extra_css = """ + extra_css = ''' p{text-align: justify; font-size: 100%} body{ text-align: left; font-size:100% } h3{font-family: sans-serif; font-size:120%; font-weight:bold; text-align: justify; } h2{font-family: sans-serif; font-size:100%; font-weight:bold; text-align: justify; } h1{font-family: sans-serif; font-size:150%; font-weight:bold; text-align: justify; } - """ + ''' feeds = [ diff --git a/recipes/acrimed.recipe b/recipes/acrimed.recipe index da796681e4..d3229562bd 100644 --- a/recipes/acrimed.recipe +++ b/recipes/acrimed.recipe @@ -28,6 +28,6 @@ class Acrimed(BasicNewsRecipe): lambda m: '' + m.group(1) + ''), (re.compile(r'

(.*) - Acrimed \| Action Critique M.*dias

'), lambda m: '

' + m.group(1) + '

')] - extra_css = """ + extra_css = ''' .chapo{font-style:italic; margin: 1em 0 0.5em} - """ + ''' diff --git a/recipes/adventuregamers.recipe b/recipes/adventuregamers.recipe index e1c5ddfd20..7a019c1512 100644 --- a/recipes/adventuregamers.recipe +++ b/recipes/adventuregamers.recipe @@ -21,7 +21,7 @@ class AdventureGamers(BasicNewsRecipe): remove_javascript = True use_embedded_content = False INDEX = u'http://www.adventuregamers.com' - extra_css = """ + extra_css = ''' .pageheader_type{font-size: x-large; font-weight: bold; color: #828D74} .pageheader_title,.page_title{font-size: xx-large; color: #394128} .pageheader_byline{font-size: small; font-weight: bold; color: #394128} @@ -32,7 +32,7 @@ class AdventureGamers(BasicNewsRecipe): .score_header{font-size: large; color: #50544A} img{margin-bottom: 1em;} body{font-family: 'Open Sans',Helvetica,Arial,sans-serif} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/afr.recipe b/recipes/afr.recipe index d43cb046cb..ef4e31b578 100644 --- a/recipes/afr.recipe +++ b/recipes/afr.recipe @@ -14,7 +14,7 @@ class afr(BasicNewsRecipe): description = ( 'For more than 65 years The Australian Financial Review has been the authority on business,' ' finance and investment news in Australia. It has a reputation for independent, award-winning ' - 'journalism and is essential reading for Australia\'s business and investor community.' + "journalism and is essential reading for Australia's business and investor community." ) masthead_url = 'https://www.nineforbrands.com.au/wp-content/uploads/2020/08/AFR-DHOSP-Logo-black-RGB.png' encoding = 'utf-8' diff --git a/recipes/afrique_21.recipe b/recipes/afrique_21.recipe index df24e4d17c..2c1e5ccbcc 100644 --- a/recipes/afrique_21.recipe +++ b/recipes/afrique_21.recipe @@ -36,9 +36,9 @@ class AfriqueXXIRecipe(BasicNewsRecipe): ''' def default_cover(self, cover_file): - """ + ''' Crée une couverture personnalisée avec le logo - """ + ''' from qt.core import QColor, QFont, QImage, QPainter, QPen, QRect, Qt from calibre.gui2 import ensure_app, load_builtin_fonts, pixmap_to_data @@ -54,7 +54,7 @@ class AfriqueXXIRecipe(BasicNewsRecipe): weekday = french_weekday[wkd] month = french_month[today.month] - date_str = f"{weekday} {today.day} {month} {today.year}" + date_str = f'{weekday} {today.day} {month} {today.year}' edition = today.strftime('Édition de %Hh') # Image de base diff --git a/recipes/al_jazeera.recipe b/recipes/al_jazeera.recipe index ed7957dccf..20817e7ca9 100644 --- a/recipes/al_jazeera.recipe +++ b/recipes/al_jazeera.recipe @@ -21,9 +21,9 @@ class AlJazeera(BasicNewsRecipe): max_articles_per_feed = 100 no_stylesheets = True use_embedded_content = False - extra_css = """ + extra_css = ''' body{font-family: Arial,sans-serif} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/al_monitor.recipe b/recipes/al_monitor.recipe index 9c3f214b6b..e9fad7b272 100644 --- a/recipes/al_monitor.recipe +++ b/recipes/al_monitor.recipe @@ -110,7 +110,7 @@ class AlMonitor(BasicNewsRecipe): title = title[0:120] + '...' href = link.get('href') if not href: - self._p("BAD HREF: " + str(link)) + self._p('BAD HREF: ' + str(link)) return self.queue_article_link(section, href, title) @@ -158,7 +158,7 @@ class AlMonitor(BasicNewsRecipe): age = (datetime.datetime.now() - date).days if (age > self.oldest_article): - return "too old" + return 'too old' return False def scrape_article_date(self, soup): @@ -174,7 +174,7 @@ class AlMonitor(BasicNewsRecipe): def date_from_string(self, datestring): try: # eg: Posted September 17, 2014 - dt = datetime.datetime.strptime(datestring, "Posted %B %d, %Y") + dt = datetime.datetime.strptime(datestring, 'Posted %B %d, %Y') except: dt = None diff --git a/recipes/albert_mohler.recipe b/recipes/albert_mohler.recipe index a85063290a..f92c54a9db 100644 --- a/recipes/albert_mohler.recipe +++ b/recipes/albert_mohler.recipe @@ -5,7 +5,7 @@ from calibre.web.feeds.news import BasicNewsRecipe class AlbertMohlersBlog(BasicNewsRecipe): - title = u'Albert Mohler\'s Blog' + title = u"Albert Mohler's Blog" __author__ = 'Peter Grungi' language = 'en' oldest_article = 90 @@ -16,5 +16,5 @@ class AlbertMohlersBlog(BasicNewsRecipe): language = 'en' author = 'Albert Mohler' - feeds = [(u'Albert Mohler\'s Blog', + feeds = [(u"Albert Mohler's Blog", u'http://feeds.feedburner.com/AlbertMohlersBlog?format=xml')] diff --git a/recipes/ald.recipe b/recipes/ald.recipe index e34e6b90ed..88d30aeac0 100644 --- a/recipes/ald.recipe +++ b/recipes/ald.recipe @@ -43,7 +43,7 @@ class ALD(BasicNewsRecipe): # Extract a list of dates from the page. # Subset this out to the list of target dates for extraction. date_list = [] - for div in soup.findAll('div', attrs={'id': "dayheader"}): + for div in soup.findAll('div', attrs={'id': 'dayheader'}): date_list.append(self.tag_to_string(div)) date_list_clean = [re.sub(r'[^\w]', ' ', date) for date in date_list] date_list_bool = [ @@ -54,14 +54,14 @@ class ALD(BasicNewsRecipe): # Process each paragraph one by one. # Stop when the text of the previous div is not in the target date list. - for div in soup.findAll('div', attrs={'class': "mobile-front"}): + for div in soup.findAll('div', attrs={'class': 'mobile-front'}): for p in div.findAll('p'): if self.tag_to_string(p.findPreviousSibling('div')) in compress_date: if p.find('a'): title = self.tag_to_string(p) link = p.find('a')['href'] if self.tag_to_string(p.findPreviousSibling('h3') - ) == "Articles of Note": + ) == 'Articles of Note': articles_note.append({ 'title': title, 'url': link, @@ -69,7 +69,7 @@ class ALD(BasicNewsRecipe): 'date': '' }) elif self.tag_to_string(p.findPreviousSibling('h3') - ) == "New Books": + ) == 'New Books': new_books.append({ 'title': title, 'url': link, diff --git a/recipes/alternatives_economiques.recipe b/recipes/alternatives_economiques.recipe index 4845283694..d341bfc683 100644 --- a/recipes/alternatives_economiques.recipe +++ b/recipes/alternatives_economiques.recipe @@ -38,7 +38,7 @@ class AlternativesEconomiques(BasicNewsRecipe): self.log('Cover URL found:', cover_url) return cover_url - self.log('Aucune couverture trouvée, utilisation de l\'image par défaut') + self.log("Aucune couverture trouvée, utilisation de l'image par défaut") return 'https://www.alternatives-economiques.fr/sites/all/themes/alternatives-economiques-main/assets/logo-alternatives-economiques.svg' except Exception as e: diff --git a/recipes/am730.recipe b/recipes/am730.recipe index c7d35ac2f9..0d79189cbf 100644 --- a/recipes/am730.recipe +++ b/recipes/am730.recipe @@ -58,7 +58,7 @@ class AM730(BasicNewsRecipe): articles = [] for aTag in soup.findAll('a',attrs={'class':'newsimglink'}): href = aTag.get('href',False) - if not href.encode("utf-8").startswith(url.encode("utf-8")) : + if not href.encode('utf-8').startswith(url.encode('utf-8')) : continue # not in same section title = href.split('/')[-1].split('-')[0] diff --git a/recipes/ambito.recipe b/recipes/ambito.recipe index 7a5a177cc6..ad83cc6605 100644 --- a/recipes/ambito.recipe +++ b/recipes/ambito.recipe @@ -28,9 +28,9 @@ class Ambito(BasicNewsRecipe): language = 'es_AR' publication_type = 'newsportal' masthead_url = 'https://www.ambito.com/css-custom/239/images/logo-239-2020v2.svg' - extra_css = """ + extra_css = ''' body{font-family: Roboto, sans-serif} - """ + ''' conversion_options = { 'comment': description, diff --git a/recipes/american_thinker.recipe b/recipes/american_thinker.recipe index 0f1480c06a..b054407f86 100644 --- a/recipes/american_thinker.recipe +++ b/recipes/american_thinker.recipe @@ -12,7 +12,7 @@ from calibre.web.feeds.news import BasicNewsRecipe class AmericanThinker(BasicNewsRecipe): title = u'American Thinker' - description = "American Thinker is a daily internet publication devoted to the thoughtful exploration of issues of importance to Americans." + description = 'American Thinker is a daily internet publication devoted to the thoughtful exploration of issues of importance to Americans.' __author__ = 'Walt Anthony' publisher = 'Thomas Lifson' category = 'news, politics, USA' diff --git a/recipes/anandtech.recipe b/recipes/anandtech.recipe index aa29ed443c..8ca0011757 100644 --- a/recipes/anandtech.recipe +++ b/recipes/anandtech.recipe @@ -39,4 +39,4 @@ class anan(BasicNewsRecipe): def print_version(self, url): # return url.replace("0Cshow0C", "0Cprint0C") # 2013-09-07 AGE: update - return url.replace("/show/", "/print/") # 2014-02-27 AGE: update + return url.replace('/show/', '/print/') # 2014-02-27 AGE: update diff --git a/recipes/ancient_egypt.recipe b/recipes/ancient_egypt.recipe index c40b0aa3cc..d88a181ea3 100644 --- a/recipes/ancient_egypt.recipe +++ b/recipes/ancient_egypt.recipe @@ -12,7 +12,7 @@ class ancientegypt(BasicNewsRecipe): language = 'en' __author__ = 'unkn0wn' description = ( - 'Ancient Egypt is the world\'s leading Egyptology magazine, exploring the history, people and culture of the Nile Valley. ' + "Ancient Egypt is the world's leading Egyptology magazine, exploring the history, people and culture of the Nile Valley. " 'Now in a larger format with a fresh new design, AE brings you the latest news and discoveries, and feature articles covering ' 'more than 5000 years of Egyptian history. Published bimonthly.' ) diff --git a/recipes/andhrajyothy_ap.recipe b/recipes/andhrajyothy_ap.recipe index 041098c8ca..f2833ddb18 100644 --- a/recipes/andhrajyothy_ap.recipe +++ b/recipes/andhrajyothy_ap.recipe @@ -75,7 +75,7 @@ class andhra(BasicNewsRecipe): url = str(snaps['OrgId']) if snaps['ObjectType'] == 4: continue - feeds_dict[section].append({"title": '', "url": url}) + feeds_dict[section].append({'title': '', 'url': url}) return [(section, articles) for section, articles in feeds_dict.items()] def preprocess_raw_html(self, raw, *a): diff --git a/recipes/andhrajyothy_tel.recipe b/recipes/andhrajyothy_tel.recipe index 37cba5ebfb..ff7195112b 100644 --- a/recipes/andhrajyothy_tel.recipe +++ b/recipes/andhrajyothy_tel.recipe @@ -75,7 +75,7 @@ class andhra(BasicNewsRecipe): url = str(snaps['OrgId']) if snaps['ObjectType'] == 4: continue - feeds_dict[section].append({"title": '', "url": url}) + feeds_dict[section].append({'title': '', 'url': url}) return [(section, articles) for section, articles in feeds_dict.items()] def preprocess_raw_html(self, raw, *a): diff --git a/recipes/arcamax.recipe b/recipes/arcamax.recipe index c255442669..aef1ca6a26 100644 --- a/recipes/arcamax.recipe +++ b/recipes/arcamax.recipe @@ -66,19 +66,19 @@ class Arcamax(BasicNewsRecipe): # (u"9 Chickweed Lane", u"https://www.arcamax.com/thefunnies/ninechickweedlane"), # (u"Agnes", u"https://www.arcamax.com/thefunnies/agnes"), # (u"Andy Capp", u"https://www.arcamax.com/thefunnies/andycapp"), - (u"BC", u"https://www.arcamax.com/thefunnies/bc"), + (u'BC', u'https://www.arcamax.com/thefunnies/bc'), # (u"Baby Blues", u"https://www.arcamax.com/thefunnies/babyblues"), # (u"Beetle Bailey", u"https://www.arcamax.com/thefunnies/beetlebailey"), - (u"Blondie", u"https://www.arcamax.com/thefunnies/blondie"), + (u'Blondie', u'https://www.arcamax.com/thefunnies/blondie'), # u"Boondocks", u"https://www.arcamax.com/thefunnies/boondocks"), # (u"Cathy", u"https://www.arcamax.com/thefunnies/cathy"), # (u"Daddys Home", u"https://www.arcamax.com/thefunnies/daddyshome"), # (u"Dinette Set", u"https://www.arcamax.com/thefunnies/thedinetteset"), - (u"Dog Eat Doug", u"https://www.arcamax.com/thefunnies/dogeatdoug"), + (u'Dog Eat Doug', u'https://www.arcamax.com/thefunnies/dogeatdoug'), # (u"Doonesbury", u"https://www.arcamax.com/thefunnies/doonesbury"), # (u"Dustin", u"https://www.arcamax.com/thefunnies/dustin"), - (u"Family Circus", u"https://www.arcamax.com/thefunnies/familycircus"), - (u"Garfield", u"https://www.arcamax.com/thefunnies/garfield"), + (u'Family Circus', u'https://www.arcamax.com/thefunnies/familycircus'), + (u'Garfield', u'https://www.arcamax.com/thefunnies/garfield'), # (u"Get Fuzzy", u"https://www.arcamax.com/thefunnies/getfuzzy"), # (u"Girls and Sports", u"https://www.arcamax.com/thefunnies/girlsandsports"), # (u"Hagar the Horrible", u"https://www.arcamax.com/thefunnies/hagarthehorrible"), @@ -87,16 +87,16 @@ class Arcamax(BasicNewsRecipe): # (u"Luann", u"https://www.arcamax.com/thefunnies/luann"), # (u"Momma", u"https://www.arcamax.com/thefunnies/momma"), # (u"Mother Goose and Grimm", u"https://www.arcamax.com/thefunnies/mothergooseandgrimm"), - (u"Mutts", u"https://www.arcamax.com/thefunnies/mutts"), + (u'Mutts', u'https://www.arcamax.com/thefunnies/mutts'), # (u"Non Sequitur", u"https://www.arcamax.com/thefunnies/nonsequitur"), # (u"Pearls Before Swine", u"https://www.arcamax.com/thefunnies/pearlsbeforeswine"), # (u"Pickles", u"https://www.arcamax.com/thefunnies/pickles"), # (u"Red and Rover", u"https://www.arcamax.com/thefunnies/redandrover"), # (u"Rubes", u"https://www.arcamax.com/thefunnies/rubes"), # (u"Rugrats", u"https://www.arcamax.com/thefunnies/rugrats"), - (u"Speed Bump", u"https://www.arcamax.com/thefunnies/speedbump"), - (u"Wizard of Id", u"https://www.arcamax.com/thefunnies/wizardofid"), - (u"Zits", u"https://www.arcamax.com/thefunnies/zits"), + (u'Speed Bump', u'https://www.arcamax.com/thefunnies/speedbump'), + (u'Wizard of Id', u'https://www.arcamax.com/thefunnies/wizardofid'), + (u'Zits', u'https://www.arcamax.com/thefunnies/zits'), ]: self.log('Finding strips for:', title) articles = self.make_links(url, title) diff --git a/recipes/arret_sur_images.recipe b/recipes/arret_sur_images.recipe index d4b3520430..70a2a21fbc 100644 --- a/recipes/arret_sur_images.recipe +++ b/recipes/arret_sur_images.recipe @@ -13,7 +13,7 @@ from calibre.web.feeds.news import BasicNewsRecipe class ArretSurImages(BasicNewsRecipe): title = 'Arrêt sur Images' - description = 'Site français d\'analyse des médias' + description = "Site français d'analyse des médias" language = 'fr' encoding = 'utf-8' needs_subscription = True @@ -27,9 +27,9 @@ class ArretSurImages(BasicNewsRecipe): ] def default_cover(self, cover_file): - """ + ''' Crée une couverture personnalisée avec le logo ASI - """ + ''' from qt.core import QColor, QFont, QImage, QPainter, QPen, QRect, Qt from calibre.gui2 import ensure_app, load_builtin_fonts, pixmap_to_data @@ -45,7 +45,7 @@ class ArretSurImages(BasicNewsRecipe): weekday = french_weekday[wkd] month = french_month[today.month] - date_str = f"{weekday} {today.day} {month} {today.year}" + date_str = f'{weekday} {today.day} {month} {today.year}' edition = today.strftime('Édition de %Hh') img = QImage(1400, 1920, QImage.Format_RGB888) @@ -123,9 +123,9 @@ class ArretSurImages(BasicNewsRecipe): br.addheaders += [('Authorization', f'Bearer {auth_response["access_token"]}')] print('Authentification réussie') else: - print('Échec de l\'authentification - Vérifiez vos identifiants') + print("Échec de l'authentification - Vérifiez vos identifiants") except Exception as e: - print(f'Erreur lors de l\'authentification: {str(e)}') + print(f"Erreur lors de l'authentification: {str(e)}") return br def get_article_url(self, article): diff --git a/recipes/asahi_shimbun_en.recipe b/recipes/asahi_shimbun_en.recipe index 7cdbfbbc8b..5f539dc5b7 100644 --- a/recipes/asahi_shimbun_en.recipe +++ b/recipes/asahi_shimbun_en.recipe @@ -1,12 +1,12 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -__license__ = "GPL v3" -__copyright__ = "2022, Albert Aparicio Isarn " +__license__ = 'GPL v3' +__copyright__ = '2022, Albert Aparicio Isarn ' -""" +''' https://www.asahi.com/ajw/ -""" +''' from datetime import datetime @@ -14,99 +14,99 @@ from calibre.web.feeds.news import BasicNewsRecipe class AsahiShimbunEnglishNews(BasicNewsRecipe): - title = "The Asahi Shimbun" - __author__ = "Albert Aparicio Isarn" + title = 'The Asahi Shimbun' + __author__ = 'Albert Aparicio Isarn' - description = ("The Asahi Shimbun is widely regarded for its journalism as the most respected daily newspaper in Japan." - " The English version offers selected articles from the vernacular Asahi Shimbun, as well as extensive" - " coverage of cool Japan,focusing on manga, travel and other timely news.") - publisher = "The Asahi Shimbun Company" - publication_type = "newspaper" - category = "news, japan" - language = "en_JP" + description = ('The Asahi Shimbun is widely regarded for its journalism as the most respected daily newspaper in Japan.' + ' The English version offers selected articles from the vernacular Asahi Shimbun, as well as extensive' + ' coverage of cool Japan,focusing on manga, travel and other timely news.') + publisher = 'The Asahi Shimbun Company' + publication_type = 'newspaper' + category = 'news, japan' + language = 'en_JP' - index = "https://www.asahi.com" - masthead_url = "https://p.potaufeu.asahi.com/ajw/css/images/en_logo@2x.png" + index = 'https://www.asahi.com' + masthead_url = 'https://p.potaufeu.asahi.com/ajw/css/images/en_logo@2x.png' oldest_article = 3 max_articles_per_feed = 40 no_stylesheets = True remove_javascript = True - remove_tags_before = {"id": "MainInner"} - remove_tags_after = {"class": "ArticleText"} - remove_tags = [{"name": "div", "class": "SnsUtilityArea"}] + remove_tags_before = {'id': 'MainInner'} + remove_tags_after = {'class': 'ArticleText'} + remove_tags = [{'name': 'div', 'class': 'SnsUtilityArea'}] def get_whats_new(self): - soup = self.index_to_soup(self.index + "/ajw/new") - news_section = soup.find("div", attrs={"class": "specialList"}) + soup = self.index_to_soup(self.index + '/ajw/new') + news_section = soup.find('div', attrs={'class': 'specialList'}) new_news = [] - for item in news_section.findAll("li"): - title = item.find("p", attrs={"class": "title"}).string - date_string = item.find("p", attrs={"class": "date"}).next + for item in news_section.findAll('li'): + title = item.find('p', attrs={'class': 'title'}).string + date_string = item.find('p', attrs={'class': 'date'}).next date = date_string.strip() - url = self.index + item.find("a")["href"] + url = self.index + item.find('a')['href'] new_news.append( { - "title": title, - "date": datetime.strptime(date, "%B %d, %Y").strftime("%Y/%m/%d"), - "url": url, - "description": "", + 'title': title, + 'date': datetime.strptime(date, '%B %d, %Y').strftime('%Y/%m/%d'), + 'url': url, + 'description': '', } ) return new_news def get_top6(self, soup): - top = soup.find("ul", attrs={"class": "top6"}) + top = soup.find('ul', attrs={'class': 'top6'}) top6_news = [] - for item in top.findAll("li"): - title = item.find("p", attrs={"class": "title"}).string - date_string = item.find("p", attrs={"class": "date"}).next + for item in top.findAll('li'): + title = item.find('p', attrs={'class': 'title'}).string + date_string = item.find('p', attrs={'class': 'date'}).next date = date_string.strip() - url = self.index + item.find("a")["href"] + url = self.index + item.find('a')['href'] top6_news.append( { - "title": title, - "date": datetime.strptime(date, "%B %d, %Y").strftime("%Y/%m/%d"), - "url": url, - "description": "", + 'title': title, + 'date': datetime.strptime(date, '%B %d, %Y').strftime('%Y/%m/%d'), + 'url': url, + 'description': '', } ) return top6_news def get_section_news(self, soup): - news_grid = soup.find("ul", attrs={"class": "default"}) + news_grid = soup.find('ul', attrs={'class': 'default'}) news = [] - for item in news_grid.findAll("li"): - title = item.find("p", attrs={"class": "title"}).string - date_string = item.find("p", attrs={"class": "date"}).next + for item in news_grid.findAll('li'): + title = item.find('p', attrs={'class': 'title'}).string + date_string = item.find('p', attrs={'class': 'date'}).next date = date_string.strip() - url = self.index + item.find("a")["href"] + url = self.index + item.find('a')['href'] news.append( { - "title": title, - "date": datetime.strptime(date, "%B %d, %Y").strftime("%Y/%m/%d"), - "url": url, - "description": "", + 'title': title, + 'date': datetime.strptime(date, '%B %d, %Y').strftime('%Y/%m/%d'), + 'url': url, + 'description': '', } ) return news def get_section(self, section): - soup = self.index_to_soup(self.index + "/ajw/" + section) + soup = self.index_to_soup(self.index + '/ajw/' + section) section_news_items = self.get_top6(soup) section_news_items.extend(self.get_section_news(soup)) @@ -114,26 +114,26 @@ class AsahiShimbunEnglishNews(BasicNewsRecipe): return section_news_items def get_special_section(self, section): - soup = self.index_to_soup(self.index + "/ajw/" + section) - top = soup.find("div", attrs={"class": "Section"}) + soup = self.index_to_soup(self.index + '/ajw/' + section) + top = soup.find('div', attrs={'class': 'Section'}) special_news = [] - for item in top.findAll("li"): - item_a = item.find("a") + for item in top.findAll('li'): + item_a = item.find('a') - text_split = item_a.text.strip().split("\n") + text_split = item_a.text.strip().split('\n') title = text_split[0] description = text_split[1].strip() - url = self.index + item_a["href"] + url = self.index + item_a['href'] special_news.append( { - "title": title, - "date": "", - "url": url, - "description": description, + 'title': title, + 'date': '', + 'url': url, + 'description': description, } ) @@ -144,24 +144,24 @@ class AsahiShimbunEnglishNews(BasicNewsRecipe): feeds = [ ("What's New", self.get_whats_new()), - ("National Report", self.get_section("national_report")), - ("Politics", self.get_section("politics")), - ("Business", self.get_section("business")), - ("Asia & World - China", self.get_section("asia_world/china")), - ("Asia & World - Korean Peninsula", self.get_section("asia_world/korean_peninsula")), - ("Asia & World - Around Asia", self.get_section("asia_world/around_asia")), - ("Asia & World - World", self.get_section("asia_world/world")), - ("Sci & Tech", self.get_section("sci_tech")), - ("Culture - Style", self.get_section("culture/style")), + ('National Report', self.get_section('national_report')), + ('Politics', self.get_section('politics')), + ('Business', self.get_section('business')), + ('Asia & World - China', self.get_section('asia_world/china')), + ('Asia & World - Korean Peninsula', self.get_section('asia_world/korean_peninsula')), + ('Asia & World - Around Asia', self.get_section('asia_world/around_asia')), + ('Asia & World - World', self.get_section('asia_world/world')), + ('Sci & Tech', self.get_section('sci_tech')), + ('Culture - Style', self.get_section('culture/style')), # ("Culture - Cooking", self.get_section("culture/cooking")), - ("Culture - Movies", self.get_section("culture/movies")), - ("Culture - Manga & Anime", self.get_section("culture/manga_anime")), - ("Travel", self.get_section("travel")), - ("Sports", self.get_section("sports")), - ("Opinion - Editorial", self.get_section("opinion/editorial")), - ("Opinion - Vox Populi", self.get_section("opinion/vox")), - ("Opinion - Views", self.get_section("opinion/views")), - ("Special", self.get_special_section("special")), + ('Culture - Movies', self.get_section('culture/movies')), + ('Culture - Manga & Anime', self.get_section('culture/manga_anime')), + ('Travel', self.get_section('travel')), + ('Sports', self.get_section('sports')), + ('Opinion - Editorial', self.get_section('opinion/editorial')), + ('Opinion - Vox Populi', self.get_section('opinion/vox')), + ('Opinion - Views', self.get_section('opinion/views')), + ('Special', self.get_special_section('special')), ] return feeds diff --git a/recipes/asianreviewofbooks.recipe b/recipes/asianreviewofbooks.recipe index dfefb5f07e..3a3565c9a3 100644 --- a/recipes/asianreviewofbooks.recipe +++ b/recipes/asianreviewofbooks.recipe @@ -26,11 +26,11 @@ class AsianReviewOfBooks(BasicNewsRecipe): publication_type = 'magazine' auto_cleanup = True masthead_url = 'https://i2.wp.com/asianreviewofbooks.com/content/wp-content/uploads/2016/09/ARBwidelogo.png' - extra_css = """ + extra_css = ''' body{font-family: "Droid Serif", serif} .entry-title {font-family: "Playfair Display", serif} img {display: block} - """ + ''' recipe_specific_options = { 'days': { diff --git a/recipes/ba_herald.recipe b/recipes/ba_herald.recipe index 91cac54aa8..40758a84e7 100644 --- a/recipes/ba_herald.recipe +++ b/recipes/ba_herald.recipe @@ -24,12 +24,12 @@ class BuenosAiresHerald(BasicNewsRecipe): publication_type = 'newspaper' masthead_url = 'http://www.buenosairesherald.com/img/logo.jpg' INDEX = 'http://www.buenosairesherald.com' - extra_css = """ + extra_css = ''' body{font-family: Arial,Helvetica,sans-serif } img{margin-bottom: 0.4em; display:block} h1{font-family: Georgia,serif} #fecha{text-align: right; font-size: small} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/bangkokpost.recipe b/recipes/bangkokpost.recipe index 464ad9800c..259a7bcb6c 100644 --- a/recipes/bangkokpost.recipe +++ b/recipes/bangkokpost.recipe @@ -16,7 +16,7 @@ class BangkokPostRecipe(BasicNewsRecipe): title = u'Bangkok Post' publisher = u'Post Publishing PCL' category = u'News' - description = u'The world\'s window to Thailand' + description = u"The world's window to Thailand" oldest_article = 7 max_articles_per_feed = 100 diff --git a/recipes/barrons.recipe b/recipes/barrons.recipe index 4ec30b325c..a80ffbcf46 100644 --- a/recipes/barrons.recipe +++ b/recipes/barrons.recipe @@ -8,11 +8,11 @@ from calibre.web.feeds.news import BasicNewsRecipe, classes, prefixed_classes class barrons(BasicNewsRecipe): - title = 'Barron\'s Magazine' + title = "Barron's Magazine" __author__ = 'unkn0wn' description = ( - 'Barron\'s is an American weekly magazine/newspaper published by Dow Jones & Company. Founded in 1921 as a sister ' - 'publication to The Wall Street Journal, Barron\'s covers U.S. financial information, market developments, and ' + "Barron's is an American weekly magazine/newspaper published by Dow Jones & Company. Founded in 1921 as a sister " + "publication to The Wall Street Journal, Barron's covers U.S. financial information, market developments, and " 'relevant statistics.' ) language = 'en_US' @@ -82,7 +82,7 @@ class barrons(BasicNewsRecipe): recipe_specific_options = { 'date': { 'short': 'The date of the edition to download (YYYYMMDD format)', - 'long': 'For example, 20240722.\nIf it didn\'t work, try again later.' + 'long': "For example, 20240722.\nIf it didn't work, try again later." } } diff --git a/recipes/bbc.recipe b/recipes/bbc.recipe index 7302d02d3f..a872d50550 100644 --- a/recipes/bbc.recipe +++ b/recipes/bbc.recipe @@ -135,9 +135,9 @@ class BBCNews(BasicNewsRecipe): # Select / de-select the feeds you want in your ebook. feeds = [ - ("News Home", "https://feeds.bbci.co.uk/news/rss.xml"), - ("UK", "https://feeds.bbci.co.uk/news/uk/rss.xml"), - ("World", "https://feeds.bbci.co.uk/news/world/rss.xml"), + ('News Home', 'https://feeds.bbci.co.uk/news/rss.xml'), + ('UK', 'https://feeds.bbci.co.uk/news/uk/rss.xml'), + ('World', 'https://feeds.bbci.co.uk/news/world/rss.xml'), # ("England", "https://feeds.bbci.co.uk/news/england/rss.xml"), # ("Scotland", "https://feeds.bbci.co.uk/news/scotland/rss.xml"), # ("Wales", "https://feeds.bbci.co.uk/news/wales/rss.xml"), @@ -147,26 +147,26 @@ class BBCNews(BasicNewsRecipe): # ("Europe", "https://feeds.bbci.co.uk/news/world/europe/rss.xml"), # ("Latin America", "https://feeds.bbci.co.uk/news/world/latin_america/rss.xml"), # ("Middle East", "https://feeds.bbci.co.uk/news/world/middle_east/rss.xml"), - ("US & Canada", "https://feeds.bbci.co.uk/news/world/us_and_canada/rss.xml"), - ("Politics", "https://feeds.bbci.co.uk/news/politics/rss.xml"), - ("Science/Environment", - "https://feeds.bbci.co.uk/news/science_and_environment/rss.xml"), - ("Technology", "https://feeds.bbci.co.uk/news/technology/rss.xml"), - ("Magazine", "https://feeds.bbci.co.uk/news/magazine/rss.xml"), - ("Entertainment/Arts", - "https://feeds.bbci.co.uk/news/entertainment_and_arts/rss.xml"), + ('US & Canada', 'https://feeds.bbci.co.uk/news/world/us_and_canada/rss.xml'), + ('Politics', 'https://feeds.bbci.co.uk/news/politics/rss.xml'), + ('Science/Environment', + 'https://feeds.bbci.co.uk/news/science_and_environment/rss.xml'), + ('Technology', 'https://feeds.bbci.co.uk/news/technology/rss.xml'), + ('Magazine', 'https://feeds.bbci.co.uk/news/magazine/rss.xml'), + ('Entertainment/Arts', + 'https://feeds.bbci.co.uk/news/entertainment_and_arts/rss.xml'), # ("Health", "https://feeds.bbci.co.uk/news/health/rss.xml"), # ("Education/Family", "https://feeds.bbci.co.uk/news/education/rss.xml"), - ("Business", "https://feeds.bbci.co.uk/news/business/rss.xml"), - ("Special Reports", "https://feeds.bbci.co.uk/news/special_reports/rss.xml"), - ("Also in the News", "https://feeds.bbci.co.uk/news/also_in_the_news/rss.xml"), + ('Business', 'https://feeds.bbci.co.uk/news/business/rss.xml'), + ('Special Reports', 'https://feeds.bbci.co.uk/news/special_reports/rss.xml'), + ('Also in the News', 'https://feeds.bbci.co.uk/news/also_in_the_news/rss.xml'), # ("Newsbeat", "https://www.bbc.co.uk/newsbeat/rss.xml"), # ("Click", "http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/programmes/click_online/rss.xml"), # ("Blog: Mark D'Arcy (Parliamentary Correspondent)", "https://feeds.bbci.co.uk/news/correspondents/markdarcy/rss.sxml"), # ("Blog: Robert Peston (Business Editor)", "https://feeds.bbci.co.uk/news/correspondents/robertpeston/rss.sxml"), # ("Blog: Stephanie Flanders (Economics Editor)", "https://feeds.bbci.co.uk/news/correspondents/stephanieflanders/rss.sxml"), - ("Sport Front Page", - "http://newsrss.bbc.co.uk/rss/sportonline_uk_edition/front_page/rss.xml"), + ('Sport Front Page', + 'http://newsrss.bbc.co.uk/rss/sportonline_uk_edition/front_page/rss.xml'), # ("Football", "http://newsrss.bbc.co.uk/rss/sportonline_uk_edition/football/rss.xml"), # ("Cricket", "http://newsrss.bbc.co.uk/rss/sportonline_uk_edition/cricket/rss.xml"), # ("Rugby Union", "http://newsrss.bbc.co.uk/rss/sportonline_uk_edition/rugby_union/rss.xml"), diff --git a/recipes/bbc_brasil.recipe b/recipes/bbc_brasil.recipe index 840c589401..6c7d4fb733 100644 --- a/recipes/bbc_brasil.recipe +++ b/recipes/bbc_brasil.recipe @@ -556,19 +556,19 @@ class BBCBrasilRecipe(BasicNewsRecipe): def print_version(self, url): # Handle sports page urls type 01: - if (url.find("go/rss/-/sport1/") != -1): - temp_url = url.replace("go/rss/-/", "") + if (url.find('go/rss/-/sport1/') != -1): + temp_url = url.replace('go/rss/-/', '') # Handle sports page urls type 02: - elif (url.find("go/rss/int/news/-/sport1/") != -1): - temp_url = url.replace("go/rss/int/news/-/", "") + elif (url.find('go/rss/int/news/-/sport1/') != -1): + temp_url = url.replace('go/rss/int/news/-/', '') # Handle regular news page urls: else: - temp_url = url.replace("go/rss/int/news/-/", "") + temp_url = url.replace('go/rss/int/news/-/', '') # Always add "?print=true" to the end of the url. - print_url = temp_url + "?print=true" + print_url = temp_url + '?print=true' return print_url diff --git a/recipes/billorielly.recipe b/recipes/billorielly.recipe index 3369cc49e2..4223df0093 100644 --- a/recipes/billorielly.recipe +++ b/recipes/billorielly.recipe @@ -30,7 +30,7 @@ class BillOReilly(BasicNewsRecipe): feeds.append(("O'Reilly Factor", articles_shows)) if articles_columns: - feeds.append(("Newspaper Column", articles_columns)) + feeds.append(('Newspaper Column', articles_columns)) return feeds diff --git a/recipes/blesk.recipe b/recipes/blesk.recipe index 8757bf4dcf..0b3380ea60 100644 --- a/recipes/blesk.recipe +++ b/recipes/blesk.recipe @@ -27,8 +27,8 @@ class bleskRecipe(BasicNewsRecipe): cover_url = 'http://img.blesk.cz/images/blesk/blesk-logo.png' remove_javascript = True no_stylesheets = True - extra_css = """ - """ + extra_css = ''' + ''' remove_attributes = [] remove_tags_before = dict(name='div', attrs={'id': ['boxContent']}) diff --git a/recipes/blic.recipe b/recipes/blic.recipe index f674695923..fbd2e463bd 100644 --- a/recipes/blic.recipe +++ b/recipes/blic.recipe @@ -23,7 +23,7 @@ class Blic(BasicNewsRecipe): masthead_url = 'http://www.blic.rs/resources/images/header/header_back.png' language = 'sr' publication_type = 'newspaper' - extra_css = """ + extra_css = ''' @font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: Georgia, serif1, serif} @@ -35,7 +35,7 @@ class Blic(BasicNewsRecipe): .potpis{font-size: x-small; color: gray} .article_info{font-size: small} img{margin-bottom: 0.8em; margin-top: 0.8em; display: block} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language, 'linearize_tables': True diff --git a/recipes/bloomberg-business-week.recipe b/recipes/bloomberg-business-week.recipe index 8d3753fc20..58a5bb185d 100644 --- a/recipes/bloomberg-business-week.recipe +++ b/recipes/bloomberg-business-week.recipe @@ -56,7 +56,7 @@ class Bloomberg(BasicNewsRecipe): masthead_url = 'https://assets.bwbx.io/s3/javelin/public/hub/images/BW-Logo-Black-cc9035fbb3.svg' description = ( 'Bloomberg Businessweek helps global leaders stay ahead with insights and in-depth analysis on the people,' - ' companies, events, and trends shaping today\'s complex, global economy.' + " companies, events, and trends shaping today's complex, global economy." ) remove_empty_feeds = True diff --git a/recipes/bookforummagazine.recipe b/recipes/bookforummagazine.recipe index cab082a8e3..f5e8cbdb98 100644 --- a/recipes/bookforummagazine.recipe +++ b/recipes/bookforummagazine.recipe @@ -2,29 +2,29 @@ from urllib.parse import urljoin from calibre.web.feeds.news import BasicNewsRecipe -_issue_url = "" +_issue_url = '' class BookforumMagazine(BasicNewsRecipe): - title = "Bookforum" + title = 'Bookforum' description = ( - "Bookforum is an American book review magazine devoted to books and " - "the discussion of literature. https://www.bookforum.com/print" + 'Bookforum is an American book review magazine devoted to books and ' + 'the discussion of literature. https://www.bookforum.com/print' ) - language = "en" - __author__ = "ping" - publication_type = "magazine" - encoding = "utf-8" + language = 'en' + __author__ = 'ping' + publication_type = 'magazine' + encoding = 'utf-8' remove_javascript = True no_stylesheets = True auto_cleanup = False compress_news_images = True compress_news_images_auto_size = 8 - keep_only_tags = [dict(class_="blog-article")] - remove_tags = [dict(name=["af-share-toggle", "af-related-articles"])] + keep_only_tags = [dict(class_='blog-article')] + remove_tags = [dict(name=['af-share-toggle', 'af-related-articles'])] - extra_css = """ + extra_css = ''' .blog-article__header { font-size: 1.8rem; margin-bottom: 0.4rem; } .blog-article__subtitle { font-size: 1.2rem; font-style: italic; margin-bottom: 1rem; } .blog-article__writer { font-size: 1rem; font-weight: bold; color: #444; } @@ -33,46 +33,46 @@ class BookforumMagazine(BasicNewsRecipe): display: block; max-width: 100%; height: auto; } .blog-article__caption { font-size: 0.8rem; display: block; margin-top: 0.2rem; } - """ + ''' def preprocess_html(self, soup): # strip away links that's not needed - for ele in soup.select(".blog-article__header a"): + for ele in soup.select('.blog-article__header a'): ele.unwrap() return soup def parse_index(self): soup = self.index_to_soup( - _issue_url if _issue_url else "https://www.bookforum.com/print" + _issue_url if _issue_url else 'https://www.bookforum.com/print' ) - meta_ele = soup.find("meta", property="og:title") + meta_ele = soup.find('meta', property='og:title') if meta_ele: self.timefmt = f' [{meta_ele["content"]}]' - cover_ele = soup.find("img", class_="toc-issue__cover") + cover_ele = soup.find('img', class_='toc-issue__cover') if cover_ele: self.cover_url = urljoin( - "https://www.bookforum.com", - soup.find("img", class_="toc-issue__cover")["src"], + 'https://www.bookforum.com', + soup.find('img', class_='toc-issue__cover')['src'], ) articles = {} - for sect_ele in soup.find_all("div", class_="toc-articles__section"): + for sect_ele in soup.find_all('div', class_='toc-articles__section'): section_name = self.tag_to_string( - sect_ele.find("a", class_="toc__anchor-links__link") + sect_ele.find('a', class_='toc__anchor-links__link') ) - for article_ele in sect_ele.find_all("article"): - title_ele = article_ele.find("h1") - sub_title_ele = article_ele.find(class_="toc-article__subtitle") + for article_ele in sect_ele.find_all('article'): + title_ele = article_ele.find('h1') + sub_title_ele = article_ele.find(class_='toc-article__subtitle') articles.setdefault(section_name, []).append( { - "title": self.tag_to_string(title_ele), - "url": article_ele.find("a", class_="toc-article__link")[ - "href" + 'title': self.tag_to_string(title_ele), + 'url': article_ele.find('a', class_='toc-article__link')[ + 'href' ], - "description": self.tag_to_string(sub_title_ele) + 'description': self.tag_to_string(sub_title_ele) if sub_title_ele - else "", + else '', } ) return articles.items() diff --git a/recipes/borsen_dk.recipe b/recipes/borsen_dk.recipe index 3a4e47f345..5414105b39 100644 --- a/recipes/borsen_dk.recipe +++ b/recipes/borsen_dk.recipe @@ -22,9 +22,9 @@ class Borsen_dk(BasicNewsRecipe): language = 'da' keep_only_tags = [ - dict(name="h1", attrs={'itemprop': 'headline'}), - dict(name="div", attrs={'itemprob': 'datePublished'}), - dict(name="div", attrs={'itemprop': 'articleBody'}), + dict(name='h1', attrs={'itemprop': 'headline'}), + dict(name='div', attrs={'itemprob': 'datePublished'}), + dict(name='div', attrs={'itemprop': 'articleBody'}), ] # Feed are found here: diff --git a/recipes/boston.com.recipe b/recipes/boston.com.recipe index 82f75c53d4..e4cbd3cd2f 100644 --- a/recipes/boston.com.recipe +++ b/recipes/boston.com.recipe @@ -42,24 +42,24 @@ def class_startswith(*prefixes): # From: https://www3.bostonglobe.com/lifestyle/comics?arc404=true comics_to_fetch = { - "ADAM@HOME": 'ad', - "ARLO & JANIS": 'aj', + 'ADAM@HOME': 'ad', + 'ARLO & JANIS': 'aj', # "CUL DE SAC": 'cds', # "CURTIS": 'kfcrt', - "DILBERT": 'dt', - "DOONESBURY": 'db', - "DUSTIN": 'kfdus', - "F MINUS": 'fm', - "FOR BETTER OR WORSE": 'fb', + 'DILBERT': 'dt', + 'DOONESBURY': 'db', + 'DUSTIN': 'kfdus', + 'F MINUS': 'fm', + 'FOR BETTER OR WORSE': 'fb', # "GET FUZZY": 'gz', # "MOTHER GOOSE & GRIMM": 'tmmgg', # "JUMPSTART": 'jt', - "MONTY": 'mt', + 'MONTY': 'mt', # "POOCH CAFE", - "RHYMES WITH ORANGE": 'kfrwo', + 'RHYMES WITH ORANGE': 'kfrwo', # "ROSE IS ROSE": 'rr', # "ZIPPY THE PINHEAD": 'kfzpy', - "ZITS": 'kfzt' + 'ZITS': 'kfzt' } @@ -77,10 +77,10 @@ def extract_json(raw_html): def absolutize_url(url): - if url.startswith("//"): - return "https:" + url + if url.startswith('//'): + return 'https:' + url if url.startswith('/'): - url = "https://www.bostonglobe.com" + url + url = 'https://www.bostonglobe.com' + url return url @@ -120,7 +120,7 @@ def main(): class BostonGlobeSubscription(BasicNewsRecipe): - title = "Boston Globe" + title = 'Boston Globe' __author__ = 'Kovid Goyal' description = 'The Boston Globe' language = 'en_US' diff --git a/recipes/boston_globe_print_edition.recipe b/recipes/boston_globe_print_edition.recipe index 8c5e81df7c..2a596c4126 100644 --- a/recipes/boston_globe_print_edition.recipe +++ b/recipes/boston_globe_print_edition.recipe @@ -25,17 +25,17 @@ def class_startswith(*prefixes): return dict(attrs={'class': q}) def absolutize_url(url): - if url.startswith("//"): - return "https:" + url + if url.startswith('//'): + return 'https:' + url if url.startswith('/'): - url = "https://www.bostonglobe.com" + url + url = 'https://www.bostonglobe.com' + url return url class BostonGlobePrint(BasicNewsRecipe): - title = "Boston Globe | Print Edition" + title = 'Boston Globe | Print Edition' __author__ = 'Kovid Goyal, unkn0wn' - description = 'The Boston Globe - Today\'s Paper' + description = "The Boston Globe - Today's Paper" language = 'en_US' keep_only_tags = [ @@ -70,7 +70,7 @@ class BostonGlobePrint(BasicNewsRecipe): for image in soup.findAll('img', src=True): if image['src'].endswith('750.jpg'): return 'https:' + image['src'] - self.log("\nCover unavailable") + self.log('\nCover unavailable') cover = None return cover @@ -94,7 +94,7 @@ class BostonGlobePrint(BasicNewsRecipe): desc = self.tag_to_string(d) self.log(section, '\n\t', title, '\n\t', desc, '\n\t\t', url) - feeds_dict[section].append({"title": title, "url": url, "description": desc}) + feeds_dict[section].append({'title': title, 'url': url, 'description': desc}) return [(section, articles) for section, articles in feeds_dict.items()] def preprocess_raw_html(self, raw_html, url): diff --git a/recipes/brewiarz.recipe b/recipes/brewiarz.recipe index ada68ebd17..4e9552ba51 100644 --- a/recipes/brewiarz.recipe +++ b/recipes/brewiarz.recipe @@ -23,40 +23,40 @@ class brewiarz(BasicNewsRecipe): next_days = 1 def parse_index(self): - dec2rom_dict = {"01": "i", "02": "ii", "03": "iii", "04": "iv", - "05": "v", "06": "vi", "07": "vii", "08": "viii", - "09": "ix", "10": "x", "11": "xi", "12": "xii"} + dec2rom_dict = {'01': 'i', '02': 'ii', '03': 'iii', '04': 'iv', + '05': 'v', '06': 'vi', '07': 'vii', '08': 'viii', + '09': 'ix', '10': 'x', '11': 'xi', '12': 'xii'} - weekday_dict = {"Sunday": "Niedziela", "Monday": "Poniedziałek", "Tuesday": "Wtorek", - "Wednesday": "Środa", "Thursday": "Czwartek", "Friday": "Piątek", "Saturday": "Sobota"} + weekday_dict = {'Sunday': 'Niedziela', 'Monday': 'Poniedziałek', 'Tuesday': 'Wtorek', + 'Wednesday': 'Środa', 'Thursday': 'Czwartek', 'Friday': 'Piątek', 'Saturday': 'Sobota'} now = datetime.datetime.now() feeds = [] for i in range(0, self.next_days): url_date = now + datetime.timedelta(days=i) - url_date_month = url_date.strftime("%m") + url_date_month = url_date.strftime('%m') url_date_month_roman = dec2rom_dict[url_date_month] - url_date_day = url_date.strftime("%d") - url_date_year = url_date.strftime("%Y")[2:] - url_date_weekday = url_date.strftime("%A") + url_date_day = url_date.strftime('%d') + url_date_year = url_date.strftime('%Y')[2:] + url_date_weekday = url_date.strftime('%A') url_date_weekday_pl = weekday_dict[url_date_weekday] - url = "http://brewiarz.pl/" + url_date_month_roman + "_" + \ - url_date_year + "/" + url_date_day + url_date_month + "/index.php3" + url = 'http://brewiarz.pl/' + url_date_month_roman + '_' + \ + url_date_year + '/' + url_date_day + url_date_month + '/index.php3' articles = self.parse_pages(url) if articles: - title = url_date_weekday_pl + " " + url_date_day + \ - "." + url_date_month + "." + url_date_year + title = url_date_weekday_pl + ' ' + url_date_day + \ + '.' + url_date_month + '.' + url_date_year feeds.append((title, articles)) else: sectors = self.get_sectors(url) for subpage in sectors: - title = url_date_weekday_pl + " " + url_date_day + "." + \ - url_date_month + "." + url_date_year + " - " + subpage.string - url = "http://brewiarz.pl/" + url_date_month_roman + "_" + url_date_year + \ - "/" + url_date_day + url_date_month + \ - "/" + subpage['href'] + title = url_date_weekday_pl + ' ' + url_date_day + '.' + \ + url_date_month + '.' + url_date_year + ' - ' + subpage.string + url = 'http://brewiarz.pl/' + url_date_month_roman + '_' + url_date_year + \ + '/' + url_date_day + url_date_month + \ + '/' + subpage['href'] print(url) articles = self.parse_pages(url) if articles: @@ -91,7 +91,7 @@ class brewiarz(BasicNewsRecipe): sublinks = ol.findAll(name='a') for sublink in sublinks: link_title = self.tag_to_string( - link) + " - " + self.tag_to_string(sublink) + link) + ' - ' + self.tag_to_string(sublink) link_url_print = re.sub( 'php3', 'php3?kr=_druk&wr=lg&', sublink['href']) link_url = url[:-10] + link_url_print @@ -145,7 +145,7 @@ class brewiarz(BasicNewsRecipe): if x == tag: break else: - print("Can't find", tag, "in", tag.parent) + print("Can't find", tag, 'in', tag.parent) continue for r in reversed(tag.contents): tag.parent.insert(i, r) diff --git a/recipes/business_insider.recipe b/recipes/business_insider.recipe index d04913ea17..e08b84b1e0 100644 --- a/recipes/business_insider.recipe +++ b/recipes/business_insider.recipe @@ -22,10 +22,10 @@ class Business_insider(BasicNewsRecipe): remove_empty_feeds = True publication_type = 'newsportal' masthead_url = 'http://static.businessinsider.com/assets/images/logos/tbi_print.jpg' - extra_css = """ + extra_css = ''' body{font-family: Arial,Helvetica,sans-serif } img{margin-bottom: 0.4em; display:block} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/business_standard_print.recipe b/recipes/business_standard_print.recipe index f4dc6d6de1..eafe84282e 100644 --- a/recipes/business_standard_print.recipe +++ b/recipes/business_standard_print.recipe @@ -64,7 +64,7 @@ class BusinessStandardPrint(BasicNewsRecipe): if dt.weekday() == 6: self.log.warn( 'Business Standard Does Not Have A Print Publication On Sunday. The Reports' - ' And Columns On This Page Today Appeared In The Newspaper\'s Saturday Edition.' + " And Columns On This Page Today Appeared In The Newspaper's Saturday Edition." ) url = 'https://apibs.business-standard.com/category/today-paper?sortBy=' + today raw = self.index_to_soup(url, raw=True) diff --git a/recipes/business_today.recipe b/recipes/business_today.recipe index b8188de06a..4db915ee96 100644 --- a/recipes/business_today.recipe +++ b/recipes/business_today.recipe @@ -90,7 +90,7 @@ class BT(BasicNewsRecipe): # Insert feeds in specified order, if available - feedSort = ['Editor\'s Note', 'Editors note'] + feedSort = ["Editor's Note", 'Editors note'] for i in feedSort: if i in sections: feeds.append((i, sections[i])) diff --git a/recipes/cacm.recipe b/recipes/cacm.recipe index aee3c68eca..201312128a 100644 --- a/recipes/cacm.recipe +++ b/recipes/cacm.recipe @@ -5,8 +5,8 @@ from calibre.web.feeds.news import BasicNewsRecipe class CACM(BasicNewsRecipe): - title = "ACM CACM Magazine" - description = "Published on day 1 of every month." + title = 'ACM CACM Magazine' + description = 'Published on day 1 of every month.' language = 'en' oldest_article = 30 max_articles_per_feed = 100 @@ -17,16 +17,16 @@ class CACM(BasicNewsRecipe): ] def get_cover_url(self): - """ + ''' Parse out cover URL from cover page. Example: From: https://cacm.acm.org/system/assets/0004/2570/April2022.Cover.1000x1338.large.jpg?1647524668&1647524668 Get: https://cacm.acm.org/system/assets/0004/2570/April2022.Cover.1000x1338.jpg - """ + ''' - soup = self.index_to_soup("https://cacm.acm.org/") - a_img = soup.find("a", class_="menuCover") - img_url = a_img.img["src"] - img_url = img_url.split("?")[0] - img_url = img_url.replace(".large", "") + soup = self.index_to_soup('https://cacm.acm.org/') + a_img = soup.find('a', class_='menuCover') + img_url = a_img.img['src'] + img_url = img_url.split('?')[0] + img_url = img_url.replace('.large', '') return img_url diff --git a/recipes/calcalist.recipe b/recipes/calcalist.recipe index 9d954f494a..933eddaf78 100644 --- a/recipes/calcalist.recipe +++ b/recipes/calcalist.recipe @@ -29,28 +29,28 @@ class AdvancedUserRecipe1283848012(BasicNewsRecipe): ] feeds = [ - (u" דף הבית", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-8,00.xml"), - (u" 24/7", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-3674,00.xml"), - (u" באזז", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-3673,00.xml"), - (u" משפט", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-3772,00.xml"), - (u" רכב", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-3783,00.xml"), - (u" אחריות וסביבה", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-3781,00.xml"), - (u" דעות", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-3791,00.xml"), - (u" תיירות ותעופה", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-3784,00.xml"), - (u" קריירה", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-3782,00.xml"), - (u" אחד העם", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-3768,00.xml"), - (u" המלצות ואזהרות", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-3771,00.xml"), - (u" הייטק והון סיכון", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-3928,00.xml"), - (u" חדשות טכנולוגיה", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-3778,00.xml"), - (u" תקשורת", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-4471,00.xml"), - (u" אינטרנט", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-3773,00.xml"), - (u" מכשירים וגאדג'טים", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-3777,00.xml"), - (u" המדריך", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-3880,00.xml"), - (u" אפליקציות", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-3998,00.xml"), - (u" Play", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-3792,00.xml"), - (u" הכסף", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-9,00.xml"), - (u" עולם", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-13,00.xml"), - (u" פרסום ושיווק", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-5,00.xml"), - (u" פנאי", u"http://www.calcalist.co.il/GeneralRSS/0,16335,L-3,00.xml"), - (u" עסקי ספורט", u"http://WallaNewsw.calcalist.co.il/GeneralRSS/0,16335,L-18,00.xml") + (u' דף הבית', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-8,00.xml'), + (u' 24/7', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-3674,00.xml'), + (u' באזז', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-3673,00.xml'), + (u' משפט', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-3772,00.xml'), + (u' רכב', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-3783,00.xml'), + (u' אחריות וסביבה', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-3781,00.xml'), + (u' דעות', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-3791,00.xml'), + (u' תיירות ותעופה', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-3784,00.xml'), + (u' קריירה', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-3782,00.xml'), + (u' אחד העם', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-3768,00.xml'), + (u' המלצות ואזהרות', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-3771,00.xml'), + (u' הייטק והון סיכון', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-3928,00.xml'), + (u' חדשות טכנולוגיה', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-3778,00.xml'), + (u' תקשורת', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-4471,00.xml'), + (u' אינטרנט', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-3773,00.xml'), + (u" מכשירים וגאדג'טים", u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-3777,00.xml'), + (u' המדריך', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-3880,00.xml'), + (u' אפליקציות', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-3998,00.xml'), + (u' Play', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-3792,00.xml'), + (u' הכסף', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-9,00.xml'), + (u' עולם', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-13,00.xml'), + (u' פרסום ושיווק', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-5,00.xml'), + (u' פנאי', u'http://www.calcalist.co.il/GeneralRSS/0,16335,L-3,00.xml'), + (u' עסקי ספורט', u'http://WallaNewsw.calcalist.co.il/GeneralRSS/0,16335,L-18,00.xml') ] diff --git a/recipes/calgary_herald.recipe b/recipes/calgary_herald.recipe index 5ab1c722fa..e74af9365a 100644 --- a/recipes/calgary_herald.recipe +++ b/recipes/calgary_herald.recipe @@ -164,24 +164,24 @@ class CanWestPaper(BasicNewsRecipe): continue break if daysback == 7: - self.log("\nCover unavailable") + self.log('\nCover unavailable') cover = None return cover def fixChars(self, string): # Replace lsquo (\x91) - fixed = re.sub("\x91", "‘", string) + fixed = re.sub('\x91', '‘', string) # Replace rsquo (\x92) - fixed = re.sub("\x92", "’", fixed) + fixed = re.sub('\x92', '’', fixed) # Replace ldquo (\x93) - fixed = re.sub("\x93", "“", fixed) + fixed = re.sub('\x93', '“', fixed) # Replace rdquo (\x94) - fixed = re.sub("\x94", "”", fixed) + fixed = re.sub('\x94', '”', fixed) # Replace ndash (\x96) - fixed = re.sub("\x96", "–", fixed) + fixed = re.sub('\x96', '–', fixed) # Replace mdash (\x97) - fixed = re.sub("\x97", "—", fixed) - fixed = re.sub("’", "’", fixed) + fixed = re.sub('\x97', '—', fixed) + fixed = re.sub('’', '’', fixed) return fixed def massageNCXText(self, description): @@ -262,10 +262,10 @@ class CanWestPaper(BasicNewsRecipe): if url.startswith('/'): url = self.url_prefix + url if not url.startswith(self.url_prefix): - print("Rejected " + url) + print('Rejected ' + url) return if url in self.url_list: - print("Rejected dup " + url) + print('Rejected dup ' + url) return self.url_list.append(url) title = self.tag_to_string(atag, False) @@ -277,8 +277,8 @@ class CanWestPaper(BasicNewsRecipe): return dtag = adiv.find('div', 'content') description = '' - print("URL " + url) - print("TITLE " + title) + print('URL ' + url) + print('TITLE ' + title) if dtag is not None: stag = dtag.span if stag is not None: @@ -286,18 +286,18 @@ class CanWestPaper(BasicNewsRecipe): description = self.tag_to_string(stag, False) else: description = self.tag_to_string(dtag, False) - print("DESCRIPTION: " + description) + print('DESCRIPTION: ' + description) if key not in articles: articles[key] = [] articles[key].append(dict( title=title, url=url, date='', description=description, author='', content='')) def parse_web_index(key, keyurl): - print("Section: " + key + ': ' + self.url_prefix + keyurl) + print('Section: ' + key + ': ' + self.url_prefix + keyurl) try: soup = self.index_to_soup(self.url_prefix + keyurl) except: - print("Section: " + key + ' NOT FOUND') + print('Section: ' + key + ' NOT FOUND') return ans.append(key) mainsoup = soup.find('div', 'bodywrapper') diff --git a/recipes/capital_gr.recipe b/recipes/capital_gr.recipe index 80bfafb953..848a24f41b 100644 --- a/recipes/capital_gr.recipe +++ b/recipes/capital_gr.recipe @@ -17,7 +17,7 @@ class Capital(BasicNewsRecipe): keep_only_tags = [ dict(name='h1'), dict(name='p'), - dict(name='span', attrs={'id': ["textbody"]}) + dict(name='span', attrs={'id': ['textbody']}) ] # 3 posts seemed to have utf8 encoding diff --git a/recipes/caravan_magazine.recipe b/recipes/caravan_magazine.recipe index 115424802d..7a3630e2fd 100644 --- a/recipes/caravan_magazine.recipe +++ b/recipes/caravan_magazine.recipe @@ -96,7 +96,7 @@ class CaravanMagazine(BasicNewsRecipe): br = BasicNewsRecipe.get_browser(self, *args, **kw) if not self.username or not self.password: return br - data = json.dumps({"0":{"json":{"email":self.username,"password":self.password}}}) + data = json.dumps({'0':{'json':{'email':self.username,'password':self.password}}}) if not isinstance(data, bytes): data = data.encode('utf-8') rq = Request( @@ -138,7 +138,7 @@ class CaravanMagazine(BasicNewsRecipe): d = self.recipe_specific_options.get('date') if d and isinstance(d, str): x = d.split('-') - inp = json.dumps({"0":{"json":{"month":int(x[0]),"year":int(x[1])}}}) + inp = json.dumps({'0':{'json':{'month':int(x[0]),'year':int(x[1])}}}) api = 'https://api.caravanmagazine.in/api/trpc/magazines.getForMonthAndYear?batch=1&input=' + quote(inp, safe='') raw = json.loads(self.index_to_soup(api, raw=True)) @@ -174,7 +174,7 @@ class CaravanMagazine(BasicNewsRecipe): def print_version(self, url): slug = urlparse(url).path - inp = json.dumps({"0":{"json":{"slug":slug}}}) + inp = json.dumps({'0':{'json':{'slug':slug}}}) return 'https://api.caravanmagazine.in/api/trpc/articles.getFromCache?batch=1&input=' + quote(inp, safe='') def preprocess_raw_html(self, raw, url): diff --git a/recipes/cato.recipe b/recipes/cato.recipe index c2d7332f17..c1a22df772 100644 --- a/recipes/cato.recipe +++ b/recipes/cato.recipe @@ -5,9 +5,9 @@ from calibre.web.feeds.news import BasicNewsRecipe class CATOInstitute(BasicNewsRecipe): title = u'The CATO Institute' - description = "The Cato Institute is a public policy research organization — a think tank — \ + description = 'The Cato Institute is a public policy research organization — a think tank — \ dedicated to the principles of individual liberty, limited government, free markets and peace.\ - Its scholars and analysts conduct independent, nonpartisan research on a wide range of policy issues." + Its scholars and analysts conduct independent, nonpartisan research on a wide range of policy issues.' __author__ = '_reader' __date__ = '05 July 2012' __version__ = '1.0' diff --git a/recipes/chr_mon.recipe b/recipes/chr_mon.recipe index 0bf344b2b3..02a91ddc1f 100644 --- a/recipes/chr_mon.recipe +++ b/recipes/chr_mon.recipe @@ -24,7 +24,7 @@ class CSMonitor(BasicNewsRecipe): remove_empty_feeds = True publication_type = 'newspaper' masthead_url = 'http://www.csmonitor.com/extension/csm_base/design/csm_design/images/csmlogo_179x46.gif' - extra_css = """ + extra_css = ''' body{font-family: Arial,Tahoma,Verdana,Helvetica,sans-serif } img{margin-bottom: 0.4em; display:block} .head {font-family: Georgia,"Times New Roman",Times,serif} @@ -32,7 +32,7 @@ class CSMonitor(BasicNewsRecipe): .hide{display: none} .sLoc{font-weight: bold} ul{list-style-type: none} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/chronicle_higher_ed.recipe b/recipes/chronicle_higher_ed.recipe index 619dbb287c..84087d3e44 100644 --- a/recipes/chronicle_higher_ed.recipe +++ b/recipes/chronicle_higher_ed.recipe @@ -39,7 +39,7 @@ class Chronicle(BasicNewsRecipe): # Go to the issue soup0 = self.index_to_soup('http://chronicle.com/section/Archives/39/') issue = soup0.find('ul', attrs={'class': 'feature-promo-list'}).li - issueurl = "http://chronicle.com" + issue.a['href'] + issueurl = 'http://chronicle.com' + issue.a['href'] # Find date dates = self.tag_to_string(issue.a).split(': ')[-1] @@ -47,12 +47,12 @@ class Chronicle(BasicNewsRecipe): # Find cover cover = soup0.find('div', attrs={ - 'class': 'side-content'}).find(attrs={'src': re.compile("photos/biz/Current")}) + 'class': 'side-content'}).find(attrs={'src': re.compile('photos/biz/Current')}) if cover is not None: - if "chronicle.com" in cover['src']: + if 'chronicle.com' in cover['src']: self.cover_url = cover['src'] else: - self.cover_url = "http://chronicle.com" + cover['src'] + self.cover_url = 'http://chronicle.com' + cover['src'] # Go to the main body soup = self.index_to_soup(issueurl) div = soup.find('div', attrs={'id': 'article-body'}) @@ -64,7 +64,7 @@ class Chronicle(BasicNewsRecipe): a = post.find('a', href=True) if a is not None: title = self.tag_to_string(a) - url = "http://chronicle.com" + a['href'].strip() + url = 'http://chronicle.com' + a['href'].strip() sectiontitle = post.findPrevious('h3') if sectiontitle is None: sectiontitle = post.findPrevious('h4') diff --git a/recipes/cicero.recipe b/recipes/cicero.recipe index 4a4acd2507..94d713820b 100644 --- a/recipes/cicero.recipe +++ b/recipes/cicero.recipe @@ -18,24 +18,24 @@ class BasicUserRecipe1316245412(BasicNewsRecipe): # remove_javascript = True remove_tags = [ - dict(name='div', attrs={'id': ["header", "navigation", "skip-link", - "header-print", "header-print-url", "meta-toolbar", "footer"]}), - dict(name='div', attrs={'class': ["region region-sidebar-first column sidebar", "breadcrumb", - "breadcrumb-title", "meta", "comment-wrapper", - "field field-name-field-show-teaser-right field-type-list-boolean field-label-above", - "page-header", - "view view-alle-karikaturen view-id-alle_karikaturen view-display-id-default view-dom-id-1", - "pagination", - "view view-letzte-videos view-id-letzte_videos view-display-id-default view-dom-id-1", - "view view-letzte-videos view-id-letzte_videos view-display-id-default view-dom-id-2", # 2011-09-23 - "view view-alle-karikaturen view-id-alle_karikaturen view-display-id-default view-dom-id-2", # 2011-09-23 + dict(name='div', attrs={'id': ['header', 'navigation', 'skip-link', + 'header-print', 'header-print-url', 'meta-toolbar', 'footer']}), + dict(name='div', attrs={'class': ['region region-sidebar-first column sidebar', 'breadcrumb', + 'breadcrumb-title', 'meta', 'comment-wrapper', + 'field field-name-field-show-teaser-right field-type-list-boolean field-label-above', + 'page-header', + 'view view-alle-karikaturen view-id-alle_karikaturen view-display-id-default view-dom-id-1', + 'pagination', + 'view view-letzte-videos view-id-letzte_videos view-display-id-default view-dom-id-1', + 'view view-letzte-videos view-id-letzte_videos view-display-id-default view-dom-id-2', # 2011-09-23 + 'view view-alle-karikaturen view-id-alle_karikaturen view-display-id-default view-dom-id-2', # 2011-09-23 ]}), - dict(name='div', attrs={'title': ["Dossier Auswahl"]}), - dict(name='h2', attrs={'class': ["title comment-form"]}), + dict(name='div', attrs={'title': ['Dossier Auswahl']}), + dict(name='h2', attrs={'class': ['title comment-form']}), dict(name='form', attrs={ - 'class': ["comment-form user-info-from-cookie"]}), + 'class': ['comment-form user-info-from-cookie']}), dict(name='table', attrs={ - 'class': ["mcx-social-horizontal", "page-header"]}), + 'class': ['mcx-social-horizontal', 'page-header']}), ] feeds = [ diff --git a/recipes/cincinnati_enquirer.recipe b/recipes/cincinnati_enquirer.recipe index 4e2409024c..2fcad1c635 100644 --- a/recipes/cincinnati_enquirer.recipe +++ b/recipes/cincinnati_enquirer.recipe @@ -34,7 +34,7 @@ class AdvancedUserRecipe1234144423(BasicNewsRecipe): dict(name='div', attrs={'class': ['padding', 'sidebar-photo', 'blog caitlin']})] remove_tags = [ - dict(name=['object', 'link', 'table', 'embed']), dict(name='div', attrs={'id': ["pluckcomments", "StoryChat"]}), dict( + dict(name=['object', 'link', 'table', 'embed']), dict(name='div', attrs={'id': ['pluckcomments', 'StoryChat']}), dict( name='div', attrs={'class': ['articleflex-container', ]}), dict(name='p', attrs={'class': ['posted', 'tags']}) ] diff --git a/recipes/ciperchile.recipe b/recipes/ciperchile.recipe index 9fb90d6fbd..c503b218bc 100644 --- a/recipes/ciperchile.recipe +++ b/recipes/ciperchile.recipe @@ -23,14 +23,14 @@ class CiperChile(BasicNewsRecipe): remove_empty_feeds = True publication_type = 'blog' masthead_url = 'http://ciperchile.cl/wp-content/themes/cipertheme/css/ui/ciper-logo.png' - extra_css = """ + extra_css = ''' body{font-family: Arial,sans-serif} .excerpt{font-family: Georgia,"Times New Roman",Times,serif; font-style: italic; font-size: 1.25em} .author{font-family: Georgia,"Times New Roman",Times,serif; font-style: italic; font-size: small} .date{font-family: Georgia,"Times New Roman",Times,serif; font-size: small; color: grey} .epigrafe{font-size: small; color: grey} img{margin-bottom: 0.4em; display:block} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/clarin.recipe b/recipes/clarin.recipe index da37fd8ad7..4138eca284 100644 --- a/recipes/clarin.recipe +++ b/recipes/clarin.recipe @@ -44,7 +44,7 @@ class Clarin(BasicNewsRecipe): # To get all the data (images) auto_cleanup = False - extra_css = """ + extra_css = ''' h1#title { line-height: 1em; margin: 0 0 .5em 0; @@ -64,7 +64,7 @@ class Clarin(BasicNewsRecipe): font-size: .9em; margin-bottom: .5em; } - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/cnetjapan.recipe b/recipes/cnetjapan.recipe index e1e4d79827..6dbcb927e5 100644 --- a/recipes/cnetjapan.recipe +++ b/recipes/cnetjapan.recipe @@ -25,16 +25,16 @@ class CNetJapan(BasicNewsRecipe): lambda match: ''), ] - remove_tags_before = dict(id="contents_l") + remove_tags_before = dict(id='contents_l') remove_tags = [ - {'class': "social_bkm_share"}, - {'class': "social_bkm_print"}, - {'class': "block20 clearfix"}, - dict(name="div", attrs={'id': 'bookreview'}), - {'class': "tag_left_ttl"}, - {'class': "tag_right"} + {'class': 'social_bkm_share'}, + {'class': 'social_bkm_print'}, + {'class': 'block20 clearfix'}, + dict(name='div', attrs={'id': 'bookreview'}), + {'class': 'tag_left_ttl'}, + {'class': 'tag_right'} ] - remove_tags_after = {'class': "block20"} + remove_tags_after = {'class': 'block20'} def parse_feeds(self): diff --git a/recipes/cnetjapan_digital.recipe b/recipes/cnetjapan_digital.recipe index 9cb2a148b4..db10032de9 100644 --- a/recipes/cnetjapan_digital.recipe +++ b/recipes/cnetjapan_digital.recipe @@ -25,16 +25,16 @@ class CNetJapanDigital(BasicNewsRecipe): lambda match: ''), ] - remove_tags_before = dict(id="contents_l") + remove_tags_before = dict(id='contents_l') remove_tags = [ - {'class': "social_bkm_share"}, - {'class': "social_bkm_print"}, - {'class': "block20 clearfix"}, - dict(name="div", attrs={'id': 'bookreview'}), - {'class': "tag_left_ttl"}, - {'class': "tag_right"} + {'class': 'social_bkm_share'}, + {'class': 'social_bkm_print'}, + {'class': 'block20 clearfix'}, + dict(name='div', attrs={'id': 'bookreview'}), + {'class': 'tag_left_ttl'}, + {'class': 'tag_right'} ] - remove_tags_after = {'class': "block20"} + remove_tags_after = {'class': 'block20'} def parse_feeds(self): diff --git a/recipes/cnetjapan_release.recipe b/recipes/cnetjapan_release.recipe index 4b85d24b9b..1cf29aef02 100644 --- a/recipes/cnetjapan_release.recipe +++ b/recipes/cnetjapan_release.recipe @@ -25,15 +25,15 @@ class CNetJapanRelease(BasicNewsRecipe): lambda match: ''), ] - remove_tags_before = dict(id="contents_l") + remove_tags_before = dict(id='contents_l') remove_tags = [ - {'class': "social_bkm_share"}, - {'class': "social_bkm_print"}, - {'class': "block20 clearfix"}, - dict(name="div", attrs={'id': 'bookreview'}), - {'class': "tag_left_ttl"} + {'class': 'social_bkm_share'}, + {'class': 'social_bkm_print'}, + {'class': 'block20 clearfix'}, + dict(name='div', attrs={'id': 'bookreview'}), + {'class': 'tag_left_ttl'} ] - remove_tags_after = {'class': "block20"} + remove_tags_after = {'class': 'block20'} def parse_feeds(self): diff --git a/recipes/cnetnews.recipe b/recipes/cnetnews.recipe index aefb2fda96..a98034ca21 100644 --- a/recipes/cnetnews.recipe +++ b/recipes/cnetnews.recipe @@ -56,7 +56,7 @@ class CnetNews(BasicNewsRecipe): keep_only_tags = [ dict(name='h1'), dict(section='author'), - dict(id=["article-body", 'cnetReview']), + dict(id=['article-body', 'cnetReview']), dict(attrs={'class': 'deal-content'}), ] diff --git a/recipes/cnn.recipe b/recipes/cnn.recipe index 9089c9d2bb..b8a80131cc 100644 --- a/recipes/cnn.recipe +++ b/recipes/cnn.recipe @@ -72,7 +72,7 @@ class CNN(BasicNewsRecipe): try: br.open(masthead) except: - self.log("\nCover unavailable") + self.log('\nCover unavailable') masthead = None return masthead diff --git a/recipes/contretemps.recipe b/recipes/contretemps.recipe index fb72855ba0..8c96314279 100644 --- a/recipes/contretemps.recipe +++ b/recipes/contretemps.recipe @@ -36,9 +36,9 @@ class ContretempsRecipe(BasicNewsRecipe): return None def default_cover(self, cover_file): - """ + ''' Crée une couverture personnalisée pour Contretemps - """ + ''' from qt.core import QColor, QFont, QImage, QPainter, QPen, QRect, Qt from calibre.gui2 import ensure_app, load_builtin_fonts, pixmap_to_data @@ -56,7 +56,7 @@ class ContretempsRecipe(BasicNewsRecipe): weekday = french_weekday[wkd] month = french_month[today.month] - date_str = f"{weekday} {today.day} {month} {today.year}" + date_str = f'{weekday} {today.day} {month} {today.year}' edition = today.strftime('Édition de %Hh%M') # Création de l'image de base (ratio ~1.6 pour format livre) diff --git a/recipes/cosmos.recipe b/recipes/cosmos.recipe index b973c027bd..0100149ed0 100644 --- a/recipes/cosmos.recipe +++ b/recipes/cosmos.recipe @@ -5,10 +5,10 @@ from calibre.web.feeds.news import BasicNewsRecipe class CosmosMagazine(BasicNewsRecipe): - title = "Cosmos Magazine" + title = 'Cosmos Magazine' description = ( - "Cosmos is a quarterly science magazine with 4 editions a year (Mar, Jun, Sep, Dec)." - "It is produced by The Royal Institution of Australia Inc (RiAus)." + 'Cosmos is a quarterly science magazine with 4 editions a year (Mar, Jun, Sep, Dec).' + 'It is produced by The Royal Institution of Australia Inc (RiAus).' ) language = 'en_AU' __author__ = 'yodha8' diff --git a/recipes/courrierinternational.recipe b/recipes/courrierinternational.recipe index 5aa05d55dc..193783171b 100644 --- a/recipes/courrierinternational.recipe +++ b/recipes/courrierinternational.recipe @@ -70,12 +70,12 @@ class CourrierInternational(BasicNewsRecipe): } ''' - needs_subscription = "optional" + needs_subscription = 'optional' login_url = 'http://www.courrierinternational.com/login' def get_browser(self): def is_form_login(form): - return "id" in form.attrs and form.attrs['id'] == "user-login-form" + return 'id' in form.attrs and form.attrs['id'] == 'user-login-form' br = BasicNewsRecipe.get_browser(self) if self.username: br.open(self.login_url) @@ -86,8 +86,8 @@ class CourrierInternational(BasicNewsRecipe): return br def preprocess_html(self, soup): - for link in soup.findAll("a", href=re.compile('^/')): - link["href"] = 'http://www.courrierinternational.com' + link["href"] + for link in soup.findAll('a', href=re.compile('^/')): + link['href'] = 'http://www.courrierinternational.com' + link['href'] return soup feeds = [ diff --git a/recipes/cubadebate.recipe b/recipes/cubadebate.recipe index ea87b6688c..335245ca4e 100644 --- a/recipes/cubadebate.recipe +++ b/recipes/cubadebate.recipe @@ -21,10 +21,10 @@ class CubaDebate(BasicNewsRecipe): encoding = 'utf-8' masthead_url = 'http://www.cubadebate.cu/wp-content/themes/cubadebate/images/logo.gif' publication_type = 'newsportal' - extra_css = """ + extra_css = ''' #BlogTitle{font-size: xx-large; font-weight: bold} body{font-family: Verdana, Arial, Tahoma, sans-serif} - """ + ''' conversion_options = { 'comments': description, 'tags': category, 'language': language, 'publisher': publisher diff --git a/recipes/dainik_bhaskar.recipe b/recipes/dainik_bhaskar.recipe index d9389c234b..129fd0ce2e 100644 --- a/recipes/dainik_bhaskar.recipe +++ b/recipes/dainik_bhaskar.recipe @@ -23,7 +23,7 @@ class DainikBhaskar(BasicNewsRecipe): soup = self.index_to_soup('https://epaper.bhaskar.com/') tag = soup.find(attrs={'class': 'scaleDiv'}) if tag: - self.cover_url = tag.find('img')['src'].replace("_ss.jpg", "_l.jpg") + self.cover_url = tag.find('img')['src'].replace('_ss.jpg', '_l.jpg') return super().get_cover_url() keep_only_tags = [ diff --git a/recipes/danas.recipe b/recipes/danas.recipe index cd6e8c8c9a..92ef429ba0 100644 --- a/recipes/danas.recipe +++ b/recipes/danas.recipe @@ -31,11 +31,11 @@ class Danas(BasicNewsRecipe): auto_cleanup = True auto_cleanup_keep = '//div[@class="post-intro-above"] //h1[@class="post-title"] | //div[@class="post-intro-title"] | //div[@class="post-meta-wrapper"]' resolve_internal_links = True - extra_css = """ + extra_css = ''' .author{font-size: small} .published {font-size: small} img{margin-bottom: 0.8em} - """ + ''' conversion_options = { 'comment': description, @@ -66,7 +66,7 @@ class Danas(BasicNewsRecipe): 'avgust', 'septembar', 'oktobar', 'novembar', 'decembar'] td = date.today() monthname = months[td.month - 1] - lurl = td.strftime("https://www.danas.rs/naslovna/naslovna-strana-za-%d-" + monthname + "-%Y/") + lurl = td.strftime('https://www.danas.rs/naslovna/naslovna-strana-za-%d-' + monthname + '-%Y/') soup = self.index_to_soup(lurl) al = soup.find('div', attrs={'class':'corax-image'}) if al and al.img: diff --git a/recipes/degentenaar.recipe b/recipes/degentenaar.recipe index 13f93f778a..fd42704dad 100644 --- a/recipes/degentenaar.recipe +++ b/recipes/degentenaar.recipe @@ -77,9 +77,9 @@ class DeGentenaarOnline(BasicNewsRecipe): soup.html['lang'] = self.lang soup.html['dir'] = self.direction mlang = new_tag(soup, 'meta', [ - ("http-equiv", "Content-Language"), ("content", self.lang)]) + ('http-equiv', 'Content-Language'), ('content', self.lang)]) mcharset = new_tag(soup, 'meta', [ - ("http-equiv", "Content-Type"), ("content", "text/html; charset=utf-8")]) + ('http-equiv', 'Content-Type'), ('content', 'text/html; charset=utf-8')]) soup.head.insert(0, mlang) soup.head.insert(1, mcharset) return soup diff --git a/recipes/democracy_journal.recipe b/recipes/democracy_journal.recipe index 96c80c946c..965947d0df 100644 --- a/recipes/democracy_journal.recipe +++ b/recipes/democracy_journal.recipe @@ -16,8 +16,8 @@ class AdvancedUserRecipe1361743898(BasicNewsRecipe): def parse_index(self): articles = [] feeds = [] - soup = self.index_to_soup("http://www.democracyjournal.org") - for x in soup.findAll(href=re.compile(r"http://www\.democracyjournal\.org/\d*/.*php$")): + soup = self.index_to_soup('http://www.democracyjournal.org') + for x in soup.findAll(href=re.compile(r'http://www\.democracyjournal\.org/\d*/.*php$')): url = x.get('href') title = self.tag_to_string(x) articles.append({'title': title, 'url': url, diff --git a/recipes/demorgen_be.recipe b/recipes/demorgen_be.recipe index 98e77f97c3..8d7eab72a4 100644 --- a/recipes/demorgen_be.recipe +++ b/recipes/demorgen_be.recipe @@ -1,8 +1,8 @@ #!/usr/bin/env python -""" +''' demorgen.be -""" +''' from calibre.web.feeds.news import BasicNewsRecipe @@ -13,7 +13,7 @@ class DeMorganBe(BasicNewsRecipe): description = 'News from Belgium in Dutch' oldest_article = 1 language = 'nl_BE' - encoding = "utf-8" + encoding = 'utf-8' max_articles_per_feed = 100 no_stylesheets = True remove_attributes = ['style', 'height', 'width'] @@ -23,10 +23,10 @@ class DeMorganBe(BasicNewsRecipe): masthead_url = 'https://www.demorgen.be/_next/static/media/demorgen_logo.dce579e2.svg' cover_url = 'https://usercontent.one/wp/www.insidejazz.be/wp-content/uploads/2018/11/pic0143.png' - extra_css = """ + extra_css = ''' time, [data-test-id:"article-label"], [data-test-id:"article-sublabel"], [[data-test-id:"article-author"]] { font-size:small; } [data-test-id:"header-intro"] { font-style: italic; } - """ + ''' keep_only_tags = [ dict(name='article', attrs={'id': 'article-content'}), diff --git a/recipes/denik.cz.recipe b/recipes/denik.cz.recipe index 2af252fc9a..b856914ab8 100644 --- a/recipes/denik.cz.recipe +++ b/recipes/denik.cz.recipe @@ -23,8 +23,8 @@ class ceskyDenikRecipe(BasicNewsRecipe): cover_url = 'http://g.denik.cz/images/loga/denik.png' remove_javascript = True no_stylesheets = True - extra_css = """ - """ + extra_css = ''' + ''' remove_tags = [] keep_only_tags = [dict(name='div', attrs={'class': 'content'})] diff --git a/recipes/denikn.cz.recipe b/recipes/denikn.cz.recipe index 50c7b9fd3e..a857cb98a2 100644 --- a/recipes/denikn.cz.recipe +++ b/recipes/denikn.cz.recipe @@ -11,11 +11,11 @@ CZ_MONTHS = ['led', 'úno', 'bře', 'dub', 'kvě', 'čen', 'čec', 'srp', 'zář def cz_title_time(): - """ + ''' Helper function to return date with czech locale. Uses hardcoded lookup table of day and month names as strftime requires locale change that is not thread safe. - """ + ''' today = datetime.today() weekday = CZ_DAYS[today.weekday()] month = CZ_MONTHS[today.month-1] @@ -26,9 +26,9 @@ def cz_title_time(): class DenikNRecipe(BasicNewsRecipe): - """ + ''' Recipe for the RSS feed of https://denikn.cz/ - """ + ''' title = u'Deník N' __author__ = 'Robert Mihaly' diff --git a/recipes/deredactie.recipe b/recipes/deredactie.recipe index 1f6a1e5316..8fe8229a29 100644 --- a/recipes/deredactie.recipe +++ b/recipes/deredactie.recipe @@ -31,13 +31,13 @@ class deredactie(BasicNewsRecipe): catnames = {} soup = self.index_to_soup( 'http://www.deredactie.be/cm/vrtnieuws.deutsch') - for elem in soup.findAll('li', attrs={'id': re.compile("^navItem[2-9]")}): + for elem in soup.findAll('li', attrs={'id': re.compile('^navItem[2-9]')}): a = elem.find('a', href=True) m = re.search('(?<=/)[^/]*$', a['href']) cat = str(m.group(0)) categories.append(cat) catnames[cat] = a['title'] - self.log("found cat %s\n" % catnames[cat]) + self.log('found cat %s\n' % catnames[cat]) feeds = [] @@ -45,7 +45,7 @@ class deredactie(BasicNewsRecipe): articles = [] soup = self.index_to_soup( 'http://www.deredactie.be/cm/vrtnieuws.deutsch/' + cat) - for a in soup.findAll('a', attrs={'href': re.compile("deutsch.*/[0-9][0-9][0-9][0-9][0-9][0-9]_")}): + for a in soup.findAll('a', attrs={'href': re.compile('deutsch.*/[0-9][0-9][0-9][0-9][0-9][0-9]_')}): skip_this_article = False url = a['href'].strip() if url.startswith('/'): @@ -55,12 +55,12 @@ class deredactie(BasicNewsRecipe): for article in articles: if article['url'] == url: skip_this_article = True - self.log("SKIPPING DUP %s" % url) + self.log('SKIPPING DUP %s' % url) break if skip_this_article: continue articles.append(myarticle) - self.log("Adding URL %s\n" % url) + self.log('Adding URL %s\n' % url) if articles: feeds.append((catnames[cat], articles)) return feeds diff --git a/recipes/dilema.recipe b/recipes/dilema.recipe index 1a64701880..b528696b09 100644 --- a/recipes/dilema.recipe +++ b/recipes/dilema.recipe @@ -34,7 +34,7 @@ class Volkskrant(BasicNewsRecipe): dict(id=['like', 'dlik']), dict(name=['script', 'noscript', 'style']), ] - remove_attributes = ["class", "id", "name", "style"] + remove_attributes = ['class', 'id', 'name', 'style'] encoding = 'utf-8' no_stylesheets = True ignore_duplicate_articles = {'url'} @@ -88,7 +88,7 @@ class Volkskrant(BasicNewsRecipe): ) ) - sections = [("Numărul curent", articles)] + sections = [('Numărul curent', articles)] return sections def preprocess_html(self, soup): diff --git a/recipes/distrowatch_weekly.recipe b/recipes/distrowatch_weekly.recipe index 836b8a3d01..03aa38ed22 100644 --- a/recipes/distrowatch_weekly.recipe +++ b/recipes/distrowatch_weekly.recipe @@ -1,8 +1,8 @@ #!/usr/bin/env python -__license__ = "GPL v3" +__license__ = 'GPL v3' -"""DistroWatch Weekly""" +'''DistroWatch Weekly''' import datetime @@ -10,28 +10,28 @@ from calibre.web.feeds.news import BasicNewsRecipe class DistroWatchWeekly(BasicNewsRecipe): - title = "DistroWatch Weekly" - description = "Weekly news about Linux distributions" - category = "Linux, Technology, News" + title = 'DistroWatch Weekly' + description = 'Weekly news about Linux distributions' + category = 'Linux, Technology, News' oldest_article = 14 - language = "en" + language = 'en' max_articles_per_feed = 50 no_stylesheets = True use_embedded_content = False - timefmt = " [%A, %d %B, %Y]" + timefmt = ' [%A, %d %B, %Y]' auto_cleanup = False keep_only_tags = [ dict( attrs={ - "class": - lambda x: x and ("News1" in x) + 'class': + lambda x: x and ('News1' in x) } ) ] def _get_mag_date(self): - """Return date of latest weekly issue.""" + '''Return date of latest weekly issue.''' d = datetime.date(2022, 6, 20) t = datetime.date.today() @@ -45,17 +45,17 @@ class DistroWatchWeekly(BasicNewsRecipe): # Get URL of latest mag page ld = self._get_mag_date() - url = ld.strftime("https://distrowatch.com/weekly.php?issue=%Y%m%d") + url = ld.strftime('https://distrowatch.com/weekly.php?issue=%Y%m%d') url = url.lower() - title = ld.strftime("DistroWatch Weekly for %Y-%m-%d") + title = ld.strftime('DistroWatch Weekly for %Y-%m-%d') # Get articles stories = [{ - "url": url, - "title": title, + 'url': url, + 'title': title, },] index = [ - ("Articles", stories), + ('Articles', stories), ] return index diff --git a/recipes/dnevnik_cro.recipe b/recipes/dnevnik_cro.recipe index b19204264f..d2e3303763 100644 --- a/recipes/dnevnik_cro.recipe +++ b/recipes/dnevnik_cro.recipe @@ -23,7 +23,7 @@ def new_tag(soup, name, attrs=()): class DnevnikCro(BasicNewsRecipe): title = 'Dnevnik - Hr' __author__ = 'Darko Miletic' - description = "Vijesti iz Hrvatske" + description = 'Vijesti iz Hrvatske' publisher = 'Dnevnik.hr' category = 'news, politics, Croatia' oldest_article = 2 @@ -67,9 +67,9 @@ class DnevnikCro(BasicNewsRecipe): del item[attrib] mlang = new_tag(soup, 'meta', [ - ("http-equiv", "Content-Language"), ("content", self.lang)]) + ('http-equiv', 'Content-Language'), ('content', self.lang)]) mcharset = new_tag(soup, 'meta', [ - ("http-equiv", "Content-Type"), ("content", "text/html; charset=UTF-8")]) + ('http-equiv', 'Content-Type'), ('content', 'text/html; charset=UTF-8')]) soup.head.insert(0, mlang) soup.head.insert(1, mcharset) return self.adeify_images(soup) diff --git a/recipes/donga.recipe b/recipes/donga.recipe index 0c55c88300..d6ac7e5537 100644 --- a/recipes/donga.recipe +++ b/recipes/donga.recipe @@ -4,15 +4,15 @@ from calibre.web.feeds.recipes import BasicNewsRecipe # Comment out sections you are not interested in sections = [ - ("정치", "politics"), - ("사회", "national"), - ("경제", "economy"), - ("국제", "international"), - ("사설칼럼", "editorials"), - ("의학과학", "science"), - ("문화연예", "culture"), - ("스포츠", "sports"), - ("사람속으로", "inmul") + ('정치', 'politics'), + ('사회', 'national'), + ('경제', 'economy'), + ('국제', 'international'), + ('사설칼럼', 'editorials'), + ('의학과학', 'science'), + ('문화연예', 'culture'), + ('스포츠', 'sports'), + ('사람속으로', 'inmul') # Following sections are marked as marked optional # as default. Uncomment to enable. # , (u'건강', 'health') @@ -26,24 +26,24 @@ sections = [ class Donga(BasicNewsRecipe): - language = "ko" - title = "동아일보" - description = "동아일보 기사" - __author__ = "Minsik Cho" - ignore_duplicate_articles = {"title", "url"} + language = 'ko' + title = '동아일보' + description = '동아일보 기사' + __author__ = 'Minsik Cho' + ignore_duplicate_articles = {'title', 'url'} compress_news_images = True no_stylesheets = True oldest_article = 2 - encoding = "utf-8" + encoding = 'utf-8' # RSS Feed in syntax: # https://rss.donga.com/[sections].xml - feeds = [(title, "https://rss.donga.com/" + section + ".xml") for (title, section) in sections] + feeds = [(title, 'https://rss.donga.com/' + section + '.xml') for (title, section) in sections] # Remove logo and print buttons remove_tags = [ - dict(name="div", attrs={"class": "popHeaderWrap"}), - dict(name="div", attrs={"class": "etc"}), + dict(name='div', attrs={'class': 'popHeaderWrap'}), + dict(name='div', attrs={'class': 'etc'}), ] def print_version(self, url): @@ -51,8 +51,8 @@ class Donga(BasicNewsRecipe): # https://www.donga.com/news/[sections]/article/all/[date]/[gid]/1 # Return print version url with syntax: # https://www.donga.com/news/View?gid=[gid]&date=[date] - reobject = re.search("(?<=/all/)([0-9]*)/([0-9]*)", url) + reobject = re.search('(?<=/all/)([0-9]*)/([0-9]*)', url) date = reobject.group(1) gid = reobject.group(2) - return "https://www.donga.com/news/View?gid=" + gid + "&date=" + date + return 'https://www.donga.com/news/View?gid=' + gid + '&date=' + date diff --git a/recipes/dr_dk.recipe b/recipes/dr_dk.recipe index 4ab0a9726e..85510a45cd 100644 --- a/recipes/dr_dk.recipe +++ b/recipes/dr_dk.recipe @@ -107,11 +107,11 @@ class DRNyheder(BasicNewsRecipe): keep_only_tags = [ - dict(name="h1", attrs={'class': 'dre-article-title__heading'}), # Title - dict(name="div", attrs={'class': 'dre-article-byline'}), # Author - dict(name="figure", attrs={'class': 'dre-standard-article__figure'}), # Comment out to remove images - dict(name="p", attrs={'class': 'dre-article-body-paragraph'}), # All body text of the article - dict(name="article", attrs={'itemtype': 'http://schema.org/NewsArticle'}), + dict(name='h1', attrs={'class': 'dre-article-title__heading'}), # Title + dict(name='div', attrs={'class': 'dre-article-byline'}), # Author + dict(name='figure', attrs={'class': 'dre-standard-article__figure'}), # Comment out to remove images + dict(name='p', attrs={'class': 'dre-article-body-paragraph'}), # All body text of the article + dict(name='article', attrs={'itemtype': 'http://schema.org/NewsArticle'}), #dict(name="h1", attrs={'class': 'hydra-latest-news-page-short-news__title'}), #dict(name="p", attrs={'class': 'hydra-latest-news-page-short-news__paragraph'}), #dict(name="div", attrs={'class': 'dre-speech'}), @@ -123,7 +123,7 @@ class DRNyheder(BasicNewsRecipe): dict(name='div', attrs={'class': [ 'hydra-latest-news-page-short-news__share', 'hydra-latest-news-page-short-news__a11y-container', 'hydra-latest-news-page-short-news__meta', 'hydra-latest-news-page-short-news__image-slider', 'dre-byline__dates']}), - dict(name="source"), + dict(name='source'), #dict(name='menu', attrs={'class': 'share'}), #dict(name='menu', attrs={'class': 'dr-site-share-horizontal'}), ] diff --git a/recipes/dzieje_pl.recipe b/recipes/dzieje_pl.recipe index 8496420553..82f8baff32 100644 --- a/recipes/dzieje_pl.recipe +++ b/recipes/dzieje_pl.recipe @@ -63,20 +63,20 @@ class Dzieje(BasicNewsRecipe): def parse_index(self): feeds = [] - feeds.append((u"Wiadomości", self.find_articles( + feeds.append((u'Wiadomości', self.find_articles( 'http://dzieje.pl/wiadomosci'))) - feeds.append((u"Kultura i sztuka", self.find_articles( + feeds.append((u'Kultura i sztuka', self.find_articles( 'http://dzieje.pl/kulturaisztuka'))) - feeds.append((u"Film", self.find_articles('http://dzieje.pl/kino'))) - feeds.append((u"Rozmaitości historyczne", + feeds.append((u'Film', self.find_articles('http://dzieje.pl/kino'))) + feeds.append((u'Rozmaitości historyczne', self.find_articles('http://dzieje.pl/rozmaitości'))) feeds.append( - (u"Książka", self.find_articles('http://dzieje.pl/ksiazka'))) + (u'Książka', self.find_articles('http://dzieje.pl/ksiazka'))) feeds.append( - (u"Wystawa", self.find_articles('http://dzieje.pl/wystawa'))) - feeds.append((u"Edukacja", self.find_articles( + (u'Wystawa', self.find_articles('http://dzieje.pl/wystawa'))) + feeds.append((u'Edukacja', self.find_articles( 'http://dzieje.pl/edukacja'))) - feeds.append((u"Dzieje się", self.find_articles( + feeds.append((u'Dzieje się', self.find_articles( 'http://dzieje.pl/wydarzenia'))) return feeds diff --git a/recipes/dziennik_pl.recipe b/recipes/dziennik_pl.recipe index d189ede4d3..e0d88ca28e 100644 --- a/recipes/dziennik_pl.recipe +++ b/recipes/dziennik_pl.recipe @@ -21,7 +21,7 @@ class Dziennik_pl(BasicNewsRecipe): remove_empty_feeds = True ignore_duplicate_articles = {'title', 'url'} extra_css = 'ul {list-style: none; padding: 0; margin: 0;} .foto {float: left;} .clr {clear: both;}' - preprocess_regexps = [(re.compile("Komentarze:"), lambda m: ''), (re.compile( + preprocess_regexps = [(re.compile('Komentarze:'), lambda m: ''), (re.compile( '

>>> CZYTAJ TAKŻE: ".*?"

'), lambda m: '')] keep_only_tags = [dict(id='article')] remove_tags = [dict(name='div', attrs={'class': ['art_box_dodatki', 'new_facebook_icons2', 'leftArt', 'article_print', 'quiz-widget', 'belka-spol', 'belka-spol belka-spol-bottom', 'art_data_tags', 'cl_right', 'boxRounded gal_inside']}), dict(name='a', attrs={'class': ['komentarz', 'article_icon_addcommnent']}), dict(name='ins'), dict(name='br')] # noqa: E501 diff --git a/recipes/dziennik_polski.recipe b/recipes/dziennik_polski.recipe index 94b76229c5..08aa849e6d 100644 --- a/recipes/dziennik_polski.recipe +++ b/recipes/dziennik_polski.recipe @@ -120,7 +120,7 @@ class DziennikPolski24(BasicNewsRecipe): if self.username is not None and self.password is not None: br.open('http://www.dziennikpolski24.pl/pl/moje-konto/950606-loguj.html') br.select_form(nr=1) - br["user_login[login]"] = self.username + br['user_login[login]'] = self.username br['user_login[pass]'] = self.password br.submit() return br diff --git a/recipes/economist.recipe b/recipes/economist.recipe index 0265dee51e..cb6aa782f4 100644 --- a/recipes/economist.recipe +++ b/recipes/economist.recipe @@ -63,7 +63,7 @@ def load_article_from_json(raw, root): body = root.xpath('//body')[0] article = E(body, 'article') E(article, 'div', data['flyTitle'], style='color: red; font-size:small; font-weight:bold;') - E(article, 'h1', data['title'], title=safe_dict(data, "url", "canonical") or '') + E(article, 'h1', data['title'], title=safe_dict(data, 'url', 'canonical') or '') E(article, 'div', data['rubric'], style='font-style: italic; color:#202020;') try: date = data['dateModified'] @@ -97,8 +97,8 @@ def process_web_node(node): return f'

{node.get("textHtml")}

' return f'

{node.get("text", "")}

' elif ntype == 'IMAGE': - alt = "" if node.get("altText") is None else node.get("altText") - cap = "" + alt = '' if node.get('altText') is None else node.get('altText') + cap = '' if node.get('caption'): if node['caption'].get('textHtml') is not None: cap = node['caption']['textHtml'] @@ -123,7 +123,7 @@ def load_article_from_web_json(raw): data = json.loads(raw)['props']['pageProps']['cp2Content'] body += f'
{data.get("flyTitle", "")}
' body += f'

{data["headline"]}

' - if data.get("rubric") and data.get("rubric") is not None: + if data.get('rubric') and data.get('rubric') is not None: body += f'
{data.get("rubric", "")}
' try: date = data['dateModified'] @@ -186,7 +186,7 @@ class Economist(BasicNewsRecipe): encoding = 'utf-8' masthead_url = 'https://www.livemint.com/lm-img/dev/economist-logo-oneline.png' - __author__ = "Kovid Goyal" + __author__ = 'Kovid Goyal' description = ( 'Global news and current affairs from a European' ' perspective. Best downloaded on Friday mornings (GMT)' @@ -199,7 +199,7 @@ class Economist(BasicNewsRecipe): resolve_internal_links = True remove_tags = [ dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent', 'aside', 'footer', 'svg']), - dict(attrs={'aria-label': "Article Teaser"}), + dict(attrs={'aria-label': 'Article Teaser'}), dict(attrs={'id': 'player'}), dict(attrs={ 'class': [ @@ -266,11 +266,11 @@ class Economist(BasicNewsRecipe): if edition_date and isinstance(edition_date, str): return parse_only_date(edition_date, as_utc=False) try: - url = self.browser.open("https://www.economist.com/printedition").geturl() + url = self.browser.open('https://www.economist.com/printedition').geturl() except Exception as e: self.log('Failed to fetch publication date with error: ' + str(e)) return super().publication_date() - return parse_only_date(url.split("/")[-1], as_utc=False) + return parse_only_date(url.split('/')[-1], as_utc=False) def economist_test_article(self): return [('Articles', [{'title':'test', @@ -364,23 +364,23 @@ class Economist(BasicNewsRecipe): self.log('Got cover:', self.cover_url, '\n', self.description) feeds_dict = defaultdict(list) - for part in safe_dict(data, "hasPart", "parts"): + for part in safe_dict(data, 'hasPart', 'parts'): try: section = part['articleSection']['internal'][0]['title'] except Exception: section = safe_dict(part, 'print', 'section', 'title') or 'section' if section not in feeds_dict: self.log(section) - title = safe_dict(part, "title") - desc = safe_dict(part, "rubric") or '' - sub = safe_dict(part, "flyTitle") or '' + title = safe_dict(part, 'title') + desc = safe_dict(part, 'rubric') or '' + sub = safe_dict(part, 'flyTitle') or '' if sub and section != sub: desc = sub + ' :: ' + desc pt = PersistentTemporaryFile('.html') pt.write(json.dumps(part).encode('utf-8')) pt.close() url = 'file:///' + pt.name - feeds_dict[section].append({"title": title, "url": url, "description": desc}) + feeds_dict[section].append({'title': title, 'url': url, 'description': desc}) self.log('\t', title, '\n\t\t', desc) return [(section, articles) for section, articles in feeds_dict.items()] @@ -513,22 +513,22 @@ class Economist(BasicNewsRecipe): return self.economist_return_index(ans) def economist_parse_web_index(self, soup): - script_tag = soup.find("script", id="__NEXT_DATA__") + script_tag = soup.find('script', id='__NEXT_DATA__') if script_tag is not None: data = json.loads(script_tag.string) # open('/t/raw.json', 'w').write(json.dumps(data, indent=2, sort_keys=True)) - self.description = safe_dict(data, "props", "pageProps", "content", "headline") - self.timefmt = ' [' + safe_dict(data, "props", "pageProps", "content", "formattedIssueDate") + ']' - self.cover_url = safe_dict(data, "props", "pageProps", "content", "cover", "url").replace( + self.description = safe_dict(data, 'props', 'pageProps', 'content', 'headline') + self.timefmt = ' [' + safe_dict(data, 'props', 'pageProps', 'content', 'formattedIssueDate') + ']' + self.cover_url = safe_dict(data, 'props', 'pageProps', 'content', 'cover', 'url').replace( 'economist.com/', 'economist.com/cdn-cgi/image/width=960,quality=80,format=auto/').replace('SQ_', '') self.log('Got cover:', self.cover_url) feeds = [] for part in safe_dict( - data, "props", "pageProps", "content", "headerSections" - ) + safe_dict(data, "props", "pageProps", "content", "sections"): - section = safe_dict(part, "name") or '' + data, 'props', 'pageProps', 'content', 'headerSections' + ) + safe_dict(data, 'props', 'pageProps', 'content', 'sections'): + section = safe_dict(part, 'name') or '' if not section: continue self.log(section) @@ -536,12 +536,12 @@ class Economist(BasicNewsRecipe): articles = [] for ar in part['articles']: - title = safe_dict(ar, "headline") or '' - url = process_url(safe_dict(ar, "url") or '') + title = safe_dict(ar, 'headline') or '' + url = process_url(safe_dict(ar, 'url') or '') if not title or not url: continue - desc = safe_dict(ar, "rubric") or '' - sub = safe_dict(ar, "flyTitle") or '' + desc = safe_dict(ar, 'rubric') or '' + sub = safe_dict(ar, 'flyTitle') or '' if sub and section != sub: desc = sub + ' :: ' + desc self.log('\t', title, '\n\t', desc, '\n\t\t', url) diff --git a/recipes/economist_espresso.recipe b/recipes/economist_espresso.recipe index 94e029f947..ab60ae5c2c 100644 --- a/recipes/economist_espresso.recipe +++ b/recipes/economist_espresso.recipe @@ -58,7 +58,7 @@ def load_article_from_json(raw, root): body = root.xpath('//body')[0] article = E(body, 'article') E(article, 'div', data['flyTitle'] , style='color: red; font-size:small; font-weight:bold;') - E(article, 'h1', data['title'], title=safe_dict(data, "url", "canonical") or '') + E(article, 'h1', data['title'], title=safe_dict(data, 'url', 'canonical') or '') E(article, 'div', data['rubric'], style='font-style: italic; color:#202020;') E(article, 'div', data['byline'], style='font-style: italic; color:#202020;') main_image_url = safe_dict(data, 'image', 'main', 'url').get('canonical') @@ -130,7 +130,7 @@ class Espresso(BasicNewsRecipe): remove_tags = [ dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent', 'aside', 'footer']), - dict(attrs={'aria-label': "Article Teaser"}), + dict(attrs={'aria-label': 'Article Teaser'}), dict(attrs={ 'class': [ 'dblClkTrk', 'ec-article-info', 'share_inline_header', @@ -189,13 +189,13 @@ class Espresso(BasicNewsRecipe): self.description = data['rubric'] ans = [] - for part in safe_dict(data, "hasPart", "parts"): - title = safe_dict(part, "title") + for part in safe_dict(data, 'hasPart', 'parts'): + title = safe_dict(part, 'title') pt = PersistentTemporaryFile('.html') pt.write(json.dumps(part).encode('utf-8')) pt.close() url = 'file:///' + pt.name - ans.append({"title": title, "url": url}) + ans.append({'title': title, 'url': url}) return [('Espresso', ans)] def preprocess_html(self, soup): diff --git a/recipes/economist_free.recipe b/recipes/economist_free.recipe index 0265dee51e..cb6aa782f4 100644 --- a/recipes/economist_free.recipe +++ b/recipes/economist_free.recipe @@ -63,7 +63,7 @@ def load_article_from_json(raw, root): body = root.xpath('//body')[0] article = E(body, 'article') E(article, 'div', data['flyTitle'], style='color: red; font-size:small; font-weight:bold;') - E(article, 'h1', data['title'], title=safe_dict(data, "url", "canonical") or '') + E(article, 'h1', data['title'], title=safe_dict(data, 'url', 'canonical') or '') E(article, 'div', data['rubric'], style='font-style: italic; color:#202020;') try: date = data['dateModified'] @@ -97,8 +97,8 @@ def process_web_node(node): return f'

{node.get("textHtml")}

' return f'

{node.get("text", "")}

' elif ntype == 'IMAGE': - alt = "" if node.get("altText") is None else node.get("altText") - cap = "" + alt = '' if node.get('altText') is None else node.get('altText') + cap = '' if node.get('caption'): if node['caption'].get('textHtml') is not None: cap = node['caption']['textHtml'] @@ -123,7 +123,7 @@ def load_article_from_web_json(raw): data = json.loads(raw)['props']['pageProps']['cp2Content'] body += f'
{data.get("flyTitle", "")}
' body += f'

{data["headline"]}

' - if data.get("rubric") and data.get("rubric") is not None: + if data.get('rubric') and data.get('rubric') is not None: body += f'
{data.get("rubric", "")}
' try: date = data['dateModified'] @@ -186,7 +186,7 @@ class Economist(BasicNewsRecipe): encoding = 'utf-8' masthead_url = 'https://www.livemint.com/lm-img/dev/economist-logo-oneline.png' - __author__ = "Kovid Goyal" + __author__ = 'Kovid Goyal' description = ( 'Global news and current affairs from a European' ' perspective. Best downloaded on Friday mornings (GMT)' @@ -199,7 +199,7 @@ class Economist(BasicNewsRecipe): resolve_internal_links = True remove_tags = [ dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent', 'aside', 'footer', 'svg']), - dict(attrs={'aria-label': "Article Teaser"}), + dict(attrs={'aria-label': 'Article Teaser'}), dict(attrs={'id': 'player'}), dict(attrs={ 'class': [ @@ -266,11 +266,11 @@ class Economist(BasicNewsRecipe): if edition_date and isinstance(edition_date, str): return parse_only_date(edition_date, as_utc=False) try: - url = self.browser.open("https://www.economist.com/printedition").geturl() + url = self.browser.open('https://www.economist.com/printedition').geturl() except Exception as e: self.log('Failed to fetch publication date with error: ' + str(e)) return super().publication_date() - return parse_only_date(url.split("/")[-1], as_utc=False) + return parse_only_date(url.split('/')[-1], as_utc=False) def economist_test_article(self): return [('Articles', [{'title':'test', @@ -364,23 +364,23 @@ class Economist(BasicNewsRecipe): self.log('Got cover:', self.cover_url, '\n', self.description) feeds_dict = defaultdict(list) - for part in safe_dict(data, "hasPart", "parts"): + for part in safe_dict(data, 'hasPart', 'parts'): try: section = part['articleSection']['internal'][0]['title'] except Exception: section = safe_dict(part, 'print', 'section', 'title') or 'section' if section not in feeds_dict: self.log(section) - title = safe_dict(part, "title") - desc = safe_dict(part, "rubric") or '' - sub = safe_dict(part, "flyTitle") or '' + title = safe_dict(part, 'title') + desc = safe_dict(part, 'rubric') or '' + sub = safe_dict(part, 'flyTitle') or '' if sub and section != sub: desc = sub + ' :: ' + desc pt = PersistentTemporaryFile('.html') pt.write(json.dumps(part).encode('utf-8')) pt.close() url = 'file:///' + pt.name - feeds_dict[section].append({"title": title, "url": url, "description": desc}) + feeds_dict[section].append({'title': title, 'url': url, 'description': desc}) self.log('\t', title, '\n\t\t', desc) return [(section, articles) for section, articles in feeds_dict.items()] @@ -513,22 +513,22 @@ class Economist(BasicNewsRecipe): return self.economist_return_index(ans) def economist_parse_web_index(self, soup): - script_tag = soup.find("script", id="__NEXT_DATA__") + script_tag = soup.find('script', id='__NEXT_DATA__') if script_tag is not None: data = json.loads(script_tag.string) # open('/t/raw.json', 'w').write(json.dumps(data, indent=2, sort_keys=True)) - self.description = safe_dict(data, "props", "pageProps", "content", "headline") - self.timefmt = ' [' + safe_dict(data, "props", "pageProps", "content", "formattedIssueDate") + ']' - self.cover_url = safe_dict(data, "props", "pageProps", "content", "cover", "url").replace( + self.description = safe_dict(data, 'props', 'pageProps', 'content', 'headline') + self.timefmt = ' [' + safe_dict(data, 'props', 'pageProps', 'content', 'formattedIssueDate') + ']' + self.cover_url = safe_dict(data, 'props', 'pageProps', 'content', 'cover', 'url').replace( 'economist.com/', 'economist.com/cdn-cgi/image/width=960,quality=80,format=auto/').replace('SQ_', '') self.log('Got cover:', self.cover_url) feeds = [] for part in safe_dict( - data, "props", "pageProps", "content", "headerSections" - ) + safe_dict(data, "props", "pageProps", "content", "sections"): - section = safe_dict(part, "name") or '' + data, 'props', 'pageProps', 'content', 'headerSections' + ) + safe_dict(data, 'props', 'pageProps', 'content', 'sections'): + section = safe_dict(part, 'name') or '' if not section: continue self.log(section) @@ -536,12 +536,12 @@ class Economist(BasicNewsRecipe): articles = [] for ar in part['articles']: - title = safe_dict(ar, "headline") or '' - url = process_url(safe_dict(ar, "url") or '') + title = safe_dict(ar, 'headline') or '' + url = process_url(safe_dict(ar, 'url') or '') if not title or not url: continue - desc = safe_dict(ar, "rubric") or '' - sub = safe_dict(ar, "flyTitle") or '' + desc = safe_dict(ar, 'rubric') or '' + sub = safe_dict(ar, 'flyTitle') or '' if sub and section != sub: desc = sub + ' :: ' + desc self.log('\t', title, '\n\t', desc, '\n\t\t', url) diff --git a/recipes/economist_news.recipe b/recipes/economist_news.recipe index e35f06dc31..a95445403f 100644 --- a/recipes/economist_news.recipe +++ b/recipes/economist_news.recipe @@ -59,7 +59,7 @@ def load_article_from_json(raw, root): body = root.xpath('//body')[0] article = E(body, 'article') E(article, 'div', data['flyTitle'], style='color: red; font-size:small; font-weight:bold;') - E(article, 'h1', data['title'], title=safe_dict(data, "url", "canonical") or '') + E(article, 'h1', data['title'], title=safe_dict(data, 'url', 'canonical') or '') E(article, 'div', data['rubric'], style='font-style: italic; color:#202020;') try: date = data['dateModified'] @@ -125,7 +125,7 @@ class EconomistNews(BasicNewsRecipe): encoding = 'utf-8' masthead_url = 'https://www.livemint.com/lm-img/dev/economist-logo-oneline.png' - __author__ = "Kovid Goyal" + __author__ = 'Kovid Goyal' description = ( 'Global news and current affairs from a European' ' perspective. Get the latest articles here.' @@ -140,7 +140,7 @@ class EconomistNews(BasicNewsRecipe): resolve_internal_links = True remove_tags = [ dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent', 'aside', 'footer', 'svg']), - dict(attrs={'aria-label': "Article Teaser"}), + dict(attrs={'aria-label': 'Article Teaser'}), dict(attrs={'id': 'player'}), dict(attrs={ 'class': [ @@ -234,9 +234,9 @@ class EconomistNews(BasicNewsRecipe): articles = [] for art in part['hasPart']['parts']: - title = safe_dict(art, "title") - desc = safe_dict(art, "rubric") or '' - sub = safe_dict(art, "flyTitle") or '' + title = safe_dict(art, 'title') + desc = safe_dict(art, 'rubric') or '' + sub = safe_dict(art, 'flyTitle') or '' if sub and section != sub: desc = sub + ' :: ' + desc if not art.get('text'): @@ -249,7 +249,7 @@ class EconomistNews(BasicNewsRecipe): pt.write(json.dumps(art).encode('utf-8')) pt.close() url = 'file:///' + pt.name - articles.append({"title": title, "url": url, "description": desc}) + articles.append({'title': title, 'url': url, 'description': desc}) self.log('\t', title, '\n\t\t', desc) if articles: feeds.append((section, articles)) diff --git a/recipes/economist_search.recipe b/recipes/economist_search.recipe index 632c8299d8..ae29fc5eab 100644 --- a/recipes/economist_search.recipe +++ b/recipes/economist_search.recipe @@ -23,8 +23,8 @@ def process_node(node): return f'

{node.get("textHtml")}

' return f'

{node.get("text", "")}

' elif ntype == 'IMAGE': - alt = "" if node.get("altText") is None else node.get("altText") - cap = "" + alt = '' if node.get('altText') is None else node.get('altText') + cap = '' if node.get('caption'): if node['caption'].get('textHtml') is not None: cap = node['caption']['textHtml'] @@ -112,7 +112,7 @@ class econ_search(BasicNewsRecipe): title = 'The Economist - Search' language = 'en' encoding = 'utf-8' - __author__ = "unkn0wn" + __author__ = 'unkn0wn' description = ( 'Use the Advanced section of the recipe to search.' ) @@ -128,7 +128,7 @@ class econ_search(BasicNewsRecipe): resolve_internal_links = True remove_tags = [ dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent', 'aside', 'footer', 'svg']), - dict(attrs={'aria-label': "Article Teaser"}), + dict(attrs={'aria-label': 'Article Teaser'}), dict(attrs={'id':'player'}), dict(attrs={ 'class': [ diff --git a/recipes/economist_world_ahead.recipe b/recipes/economist_world_ahead.recipe index ed5a798d80..8b2bd6d3fe 100644 --- a/recipes/economist_world_ahead.recipe +++ b/recipes/economist_world_ahead.recipe @@ -23,8 +23,8 @@ def process_node(node): return f'

{node.get("textHtml")}

' return f'

{node.get("text", "")}

' elif ntype == 'IMAGE': - alt = "" if node.get("altText") is None else node.get("altText") - cap = "" + alt = '' if node.get('altText') is None else node.get('altText') + cap = '' if node.get('caption'): if node['caption'].get('textHtml') is not None: cap = node['caption']['textHtml'] @@ -122,7 +122,7 @@ class EconomistWorld(BasicNewsRecipe): encoding = 'utf-8' masthead_url = 'https://www.livemint.com/lm-img/dev/economist-logo-oneline.png' - __author__ = "unkn0wn" + __author__ = 'unkn0wn' description = ( 'The World Ahead is The Economist’s future-gazing publication. It prepares audiences for what is to ' 'come with mind-stretching insights and expert analysis—all in The Economist’s clear, elegant style.' @@ -136,7 +136,7 @@ class EconomistWorld(BasicNewsRecipe): resolve_internal_links = True remove_tags = [ dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent', 'aside', 'footer', 'svg']), - dict(attrs={'aria-label': "Article Teaser"}), + dict(attrs={'aria-label': 'Article Teaser'}), dict(attrs={'id': 'player'}), dict(attrs={ 'class': [ @@ -205,24 +205,24 @@ class EconomistWorld(BasicNewsRecipe): return self.economist_return_index(ans) def economist_parse_index(self, soup): - script_tag = soup.find("script", id="__NEXT_DATA__") + script_tag = soup.find('script', id='__NEXT_DATA__') if script_tag is not None: data = json.loads(script_tag.string) # open('/t/raw.json', 'w').write(json.dumps(data, indent=2, sort_keys=True)) - self.title = safe_dict(data, "props", "pageProps", "content", "headline") + self.title = safe_dict(data, 'props', 'pageProps', 'content', 'headline') self.cover_url = 'https://mma.prnewswire.com/media/2561745/The_Economist_World_Ahead_2025_cover.jpg?w=600' feeds = [] - for coll in safe_dict(data, "props", "pageProps", "content", "components"): - section = safe_dict(coll, "headline") or '' + for coll in safe_dict(data, 'props', 'pageProps', 'content', 'components'): + section = safe_dict(coll, 'headline') or '' self.log(section) articles = [] - for part in safe_dict(coll, "items"): - title = safe_dict(part, "headline") or '' - url = process_url(safe_dict(part, "url") or '') - desc = safe_dict(part, "rubric") or '' - sub = safe_dict(part, "flyTitle") or '' + for part in safe_dict(coll, 'items'): + title = safe_dict(part, 'headline') or '' + url = process_url(safe_dict(part, 'url') or '') + desc = safe_dict(part, 'rubric') or '' + sub = safe_dict(part, 'flyTitle') or '' if sub and section != sub: desc = sub + ' :: ' + desc self.log('\t', title, '\n\t', desc, '\n\t\t', url) diff --git a/recipes/edmonton_journal.recipe b/recipes/edmonton_journal.recipe index 5395ba7bda..2c43582ad0 100644 --- a/recipes/edmonton_journal.recipe +++ b/recipes/edmonton_journal.recipe @@ -164,24 +164,24 @@ class CanWestPaper(BasicNewsRecipe): continue break if daysback == 7: - self.log("\nCover unavailable") + self.log('\nCover unavailable') cover = None return cover def fixChars(self, string): # Replace lsquo (\x91) - fixed = re.sub("\x91", "‘", string) + fixed = re.sub('\x91', '‘', string) # Replace rsquo (\x92) - fixed = re.sub("\x92", "’", fixed) + fixed = re.sub('\x92', '’', fixed) # Replace ldquo (\x93) - fixed = re.sub("\x93", "“", fixed) + fixed = re.sub('\x93', '“', fixed) # Replace rdquo (\x94) - fixed = re.sub("\x94", "”", fixed) + fixed = re.sub('\x94', '”', fixed) # Replace ndash (\x96) - fixed = re.sub("\x96", "–", fixed) + fixed = re.sub('\x96', '–', fixed) # Replace mdash (\x97) - fixed = re.sub("\x97", "—", fixed) - fixed = re.sub("’", "’", fixed) + fixed = re.sub('\x97', '—', fixed) + fixed = re.sub('’', '’', fixed) return fixed def massageNCXText(self, description): @@ -262,10 +262,10 @@ class CanWestPaper(BasicNewsRecipe): if url.startswith('/'): url = self.url_prefix + url if not url.startswith(self.url_prefix): - print("Rejected " + url) + print('Rejected ' + url) return if url in self.url_list: - print("Rejected dup " + url) + print('Rejected dup ' + url) return self.url_list.append(url) title = self.tag_to_string(atag, False) @@ -277,8 +277,8 @@ class CanWestPaper(BasicNewsRecipe): return dtag = adiv.find('div', 'content') description = '' - print("URL " + url) - print("TITLE " + title) + print('URL ' + url) + print('TITLE ' + title) if dtag is not None: stag = dtag.span if stag is not None: @@ -286,18 +286,18 @@ class CanWestPaper(BasicNewsRecipe): description = self.tag_to_string(stag, False) else: description = self.tag_to_string(dtag, False) - print("DESCRIPTION: " + description) + print('DESCRIPTION: ' + description) if key not in articles: articles[key] = [] articles[key].append(dict( title=title, url=url, date='', description=description, author='', content='')) def parse_web_index(key, keyurl): - print("Section: " + key + ': ' + self.url_prefix + keyurl) + print('Section: ' + key + ': ' + self.url_prefix + keyurl) try: soup = self.index_to_soup(self.url_prefix + keyurl) except: - print("Section: " + key + ' NOT FOUND') + print('Section: ' + key + ' NOT FOUND') return ans.append(key) mainsoup = soup.find('div', 'bodywrapper') diff --git a/recipes/el_colombiano.recipe b/recipes/el_colombiano.recipe index f557d85bbd..81c888bff9 100644 --- a/recipes/el_colombiano.recipe +++ b/recipes/el_colombiano.recipe @@ -20,12 +20,12 @@ class AdvancedUserRecipe1311790237(BasicNewsRecipe): masthead_url = 'http://www.elcolombiano.com/images/logoElColombiano348x46.gif' publication_type = 'newspaper' - extra_css = """ + extra_css = ''' p{text-align: justify; font-size: 100%} body{ text-align: left; font-size:100% } h1{font-family: sans-serif; font-size:150%; font-weight:bold; text-align: justify; } h3{font-family: sans-serif; font-size:100%; font-style: italic; text-align: justify; } - """ + ''' feeds = [(u'Portada', u'http://www.elcolombiano.com/rss/portada.xml'), (u'Antioquia', u'http://www.elcolombiano.com/rss/Antioquia.xml'), diff --git a/recipes/el_cultural.recipe b/recipes/el_cultural.recipe index fe767c507d..737aace9fc 100644 --- a/recipes/el_cultural.recipe +++ b/recipes/el_cultural.recipe @@ -55,9 +55,9 @@ class RevistaElCultural(BasicNewsRecipe): if url.startswith('/version_papel/' + titleSection + '/'): url = 'http://www.elcultural.es' + url - self.log('\t\tFound article:', title[0:title.find("|") - 1]) + self.log('\t\tFound article:', title[0:title.find('|') - 1]) self.log('\t\t\t', url) - current_articles.append({'title': title[0:title.find("|") - 1], 'url': url, + current_articles.append({'title': title[0:title.find('|') - 1], 'url': url, 'description': '', 'date': ''}) return current_articles diff --git a/recipes/el_diplo.recipe b/recipes/el_diplo.recipe index c9c44e26f0..2374a639cb 100644 --- a/recipes/el_diplo.recipe +++ b/recipes/el_diplo.recipe @@ -1,51 +1,51 @@ # -*- mode: python; coding: utf-8; -*- # vim: set syntax=python fileencoding=utf-8 -__license__ = "GPL v3" -__copyright__ = "2023, Tomás Di Domenico " +__license__ = 'GPL v3' +__copyright__ = '2023, Tomás Di Domenico ' -""" +''' www.eldiplo.org -""" +''' from calibre.web.feeds.news import BasicNewsRecipe class ElDiplo2023(BasicNewsRecipe): - title = "Le Monde Diplomatique - cono sur" - __author__ = "Tomás Di Domenico" - description = "Publicación de Le Monde Diplomatique para el cono sur." - publisher = "Capital Intelectual" - category = "News, Politics, Argentina, Uruguay, Paraguay, South America, World" + title = 'Le Monde Diplomatique - cono sur' + __author__ = 'Tomás Di Domenico' + description = 'Publicación de Le Monde Diplomatique para el cono sur.' + publisher = 'Capital Intelectual' + category = 'News, Politics, Argentina, Uruguay, Paraguay, South America, World' oldest_article = 31 no_stylesheets = True - encoding = "utf8" + encoding = 'utf8' use_embedded_content = False - language = "es_AR" + language = 'es_AR' remove_empty_feeds = True - publication_type = "magazine" + publication_type = 'magazine' delay = 1 simultaneous_downloads = 1 timeout = 8 needs_subscription = True - ignore_duplicate_articles = {"url"} + ignore_duplicate_articles = {'url'} temp_files = [] fetch_retries = 10 handle_gzip = True compress_news_images = True scale_news_images_to_device = True masthead_url = ( - "https://www.eldiplo.org/wp-content/themes/_polenta_/assets/diplo.png" + 'https://www.eldiplo.org/wp-content/themes/_polenta_/assets/diplo.png' ) - INDEX = "https://www.eldiplo.org/" + INDEX = 'https://www.eldiplo.org/' - conversion_options = {"series": "El Dipló", "publisher": publisher, "base_font_size": 8, "tags": category} + conversion_options = {'series': 'El Dipló', 'publisher': publisher, 'base_font_size': 8, 'tags': category} - keep_only_tags = [dict(name=["article"])] + keep_only_tags = [dict(name=['article'])] - remove_tags = [dict(name=["button"])] + remove_tags = [dict(name=['button'])] - extra_css = """ + extra_css = ''' .entry-title { text-align: center; } @@ -67,59 +67,59 @@ class ElDiplo2023(BasicNewsRecipe): padding-left: 10%; padding-right: 10%; } - """ + ''' def get_browser(self): br = BasicNewsRecipe.get_browser(self) br.open(self.INDEX) if self.username is not None and self.password is not None: - br.select_form(id="loginform") - br["log"] = self.username - br["pwd"] = self.password + br.select_form(id='loginform') + br['log'] = self.username + br['pwd'] = self.password br.submit() return br def get_cover_url(self): soup_index = self.index_to_soup(self.INDEX) - tag_sumario = soup_index.find("span", text="Sumario") - url_sumario = "https://www.eldiplo.org" + tag_sumario.parent["href"] + tag_sumario = soup_index.find('span', text='Sumario') + url_sumario = 'https://www.eldiplo.org' + tag_sumario.parent['href'] soup = self.index_to_soup(url_sumario) - container = soup.find("div", class_="px-16") - url = container.find("img")["src"] + container = soup.find('div', class_='px-16') + url = container.find('img')['src'] - return getattr(self, "cover_url", url) + return getattr(self, 'cover_url', url) def _process_article(self, article): - url = article.find("a", href=True, attrs={"class": "title"})["href"] - title = self.tag_to_string(article).replace("Editorial", "Editorial: ") + url = article.find('a', href=True, attrs={'class': 'title'})['href'] + title = self.tag_to_string(article).replace('Editorial', 'Editorial: ') try: - title, authors = title.split(", por") - authors = f"por {authors}" + title, authors = title.split(', por') + authors = f'por {authors}' except ValueError: - authors = "" - self.log("title: ", title, " url: ", url) - return {"title": title, "url": url, "description": authors, "date": ""} + authors = '' + self.log('title: ', title, ' url: ', url) + return {'title': title, 'url': url, 'description': authors, 'date': ''} def preprocess_html(self, soup): - font_size = "90%" + font_size = '90%' # make the footnotes smaller - for p in soup.find("div", id="nota_pie").findChildren("p", recursive=False): - p["style"] = f"font-size: {font_size};" + for p in soup.find('div', id='nota_pie').findChildren('p', recursive=False): + p['style'] = f'font-size: {font_size};' return soup def parse_index(self): soup_index = self.index_to_soup(self.INDEX) - tag_sumario = soup_index.find("span", text="Sumario") + tag_sumario = soup_index.find('span', text='Sumario') if tag_sumario is None: return None - url_sumario = "https://www.eldiplo.org" + tag_sumario.parent["href"] + url_sumario = 'https://www.eldiplo.org' + tag_sumario.parent['href'] self.log(url_sumario) soup_sumario = self.index_to_soup(url_sumario) @@ -128,20 +128,20 @@ class ElDiplo2023(BasicNewsRecipe): articles = [] dossiers = [] - sumario = soup_sumario.find("div", class_="sumario") + sumario = soup_sumario.find('div', class_='sumario') - for section in sumario.find_all("div", recursive=False): - classes = section.attrs["class"] + for section in sumario.find_all('div', recursive=False): + classes = section.attrs['class'] - if "dossier" in classes: - dtitle = self.tag_to_string(section.find("h3")) + if 'dossier' in classes: + dtitle = self.tag_to_string(section.find('h3')) darticles = [] - for article in section.find_all("div", recursive=False): + for article in section.find_all('div', recursive=False): darticles.append(self._process_article(article)) dossiers.append((dtitle, darticles)) else: articles.append(self._process_article(section)) - feeds.append(("Artículos", articles)) + feeds.append(('Artículos', articles)) feeds += dossiers return feeds diff --git a/recipes/el_pais.recipe b/recipes/el_pais.recipe index ffe83b57dc..b2d1bd90a5 100644 --- a/recipes/el_pais.recipe +++ b/recipes/el_pais.recipe @@ -119,11 +119,11 @@ div.a_md_a {text-align: center; text-transform: uppercase; font-size: .8rem;} try: br.open(cover) except: - self.log("\nCover unavailable") + self.log('\nCover unavailable') cover = None return cover def image_url_processor(cls, baseurl, url): - splitUrl = url.split("cloudfront-") + splitUrl = url.split('cloudfront-') parsedUrl = 'https://cloudfront-' + splitUrl[1] return parsedUrl diff --git a/recipes/el_pais_babelia.recipe b/recipes/el_pais_babelia.recipe index 88049a91b1..3fff2db54d 100644 --- a/recipes/el_pais_babelia.recipe +++ b/recipes/el_pais_babelia.recipe @@ -36,7 +36,7 @@ class ElPaisBabelia(BasicNewsRecipe): title = self.tag_to_string(post) if str(post).find('class=') > 0: klass = post['class'] - if klass != "": + if klass != '': self.log() self.log('--> post: ', post) self.log('--> url: ', url) diff --git a/recipes/elcohetealaluna.recipe b/recipes/elcohetealaluna.recipe index 1ff45144b5..4e33f113ef 100644 --- a/recipes/elcohetealaluna.recipe +++ b/recipes/elcohetealaluna.recipe @@ -28,12 +28,12 @@ class elcohetealaluna(BasicNewsRecipe): compress_news_images = True masthead_url = 'https://www.elcohetealaluna.com/wp-content/uploads/2018/06/logo-menu.png' - extra_css = """ + extra_css = ''' body{font-family: Georgia, Times, "Times New Roman", serif} h1,h2,.post-author-name{font-family: Oswald, sans-serif} h2{color: gray} img{margin-top:1em; margin-bottom: 1em; display:block} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/elcronista-arg.recipe b/recipes/elcronista-arg.recipe index 9cc1f908ae..4ada20b514 100644 --- a/recipes/elcronista-arg.recipe +++ b/recipes/elcronista-arg.recipe @@ -28,10 +28,10 @@ class ElCronistaArg(BasicNewsRecipe): auto_cleanup_keep = '//div[@class="header-bottom"] | //h1 | //h2' ignore_duplicate_articles = {'url'} masthead_url = 'https://www.cronista.com/export/sites/diarioelcronista/arte/v2/lg_cronista_footer.png_665574830.png' - extra_css = """ + extra_css = ''' body{font-family: 'Source Sans Pro', sans-serif} h1,h2,h3,h4{font-family: 'Libre Baskerville', serif} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/elektroda_pl.recipe b/recipes/elektroda_pl.recipe index 2a5550ae40..d5361c4407 100644 --- a/recipes/elektroda_pl.recipe +++ b/recipes/elektroda_pl.recipe @@ -29,5 +29,5 @@ class Elektroda(BasicNewsRecipe): feeds = BasicNewsRecipe.parse_feeds(self) for feed in feeds: for article in feed.articles[:]: - article.title = article.title[article.title.find("::") + 3:] + article.title = article.title[article.title.find('::') + 3:] return feeds diff --git a/recipes/elmundo.recipe b/recipes/elmundo.recipe index 1432aa1715..c4ed15ce6a 100644 --- a/recipes/elmundo.recipe +++ b/recipes/elmundo.recipe @@ -35,14 +35,14 @@ class ElMundo(BasicNewsRecipe): articles_are_obfuscated = True auto_cleanup = True temp_files = [] - extra_css = """ + extra_css = ''' body{font-family: "PT serif",Georgia,serif,times} .metadata_noticia{font-size: small} .pestana_GDP{font-size: small; font-weight:bold} h1 {color: #333333; font-family: "Clear Sans Bold",Arial,sans-serif,helvetica} .hora{color: red} .update{color: gray} - """ + ''' conversion_options = { 'comments': description, 'tags': category, 'language': language, 'publisher': publisher @@ -83,14 +83,14 @@ class ElMundo(BasicNewsRecipe): cover = self.masthead_url st = time.localtime() year = str(st.tm_year) - month = "%.2d" % st.tm_mon - day = "%.2d" % st.tm_mday + month = '%.2d' % st.tm_mon + day = '%.2d' % st.tm_mday cover = 'http://img.kiosko.net/' + year + '/' + \ month + '/' + day + '/es/elmundo.750.jpg' try: self.browser.open(cover) except: - self.log("\nPortada no disponible") + self.log('\nPortada no disponible') return cover def get_obfuscated_article(self, url): @@ -103,7 +103,7 @@ class ElMundo(BasicNewsRecipe): html = response.read() count = tries except: - print("Retrying download...") + print('Retrying download...') count += 1 if html is not None: tfile = PersistentTemporaryFile('_fa.html') diff --git a/recipes/elperiodico_spanish.recipe b/recipes/elperiodico_spanish.recipe index b6c591c48f..8aab5a542f 100644 --- a/recipes/elperiodico_spanish.recipe +++ b/recipes/elperiodico_spanish.recipe @@ -66,7 +66,7 @@ class ElPeriodico_cat(BasicNewsRecipe): def preprocess_html(self, soup): mcharset = new_tag(soup, 'meta', [ - ("http-equiv", "Content-Type"), ("content", "text/html; charset=utf-8")]) + ('http-equiv', 'Content-Type'), ('content', 'text/html; charset=utf-8')]) soup.head.insert(0, mcharset) for item in soup.findAll(style=True): del item['style'] diff --git a/recipes/en_globes_co_il.recipe b/recipes/en_globes_co_il.recipe index 2ad5aac6af..0dbe822972 100644 --- a/recipes/en_globes_co_il.recipe +++ b/recipes/en_globes_co_il.recipe @@ -18,18 +18,18 @@ class En_Globes_Recipe(BasicNewsRecipe): max_articles_per_feed = 100 feeds = [ - (u"Main Headlines", u"https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederNode?iID=942"), - (u"Israeli stocks on Wall Street", u"https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1392"), - (u"All news", u"https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=1725"), - (u"Macro economics", u"https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1389"), - (u"Aerospace and defense", u"https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1380"), - (u"Real estate", u"https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederKeyword?iID=1385"), - (u"Energy and water", u"https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1382"), - (u"Start-ups and venture capital", u"https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1397"), - (u"Financial services", u"https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1383"), - (u"Tel Aviv markets", u"https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1404"), - (u"Healthcare", u"https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1377"), - (u"Telecommunications", u"https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1386"), - (u"Information technology", u"https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1376"), - (u"Transport and infrastructure", u"https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1388"), + (u'Main Headlines', u'https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederNode?iID=942'), + (u'Israeli stocks on Wall Street', u'https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1392'), + (u'All news', u'https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=1725'), + (u'Macro economics', u'https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1389'), + (u'Aerospace and defense', u'https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1380'), + (u'Real estate', u'https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederKeyword?iID=1385'), + (u'Energy and water', u'https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1382'), + (u'Start-ups and venture capital', u'https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1397'), + (u'Financial services', u'https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1383'), + (u'Tel Aviv markets', u'https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1404'), + (u'Healthcare', u'https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1377'), + (u'Telecommunications', u'https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1386'), + (u'Information technology', u'https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1376'), + (u'Transport and infrastructure', u'https://www.globes.co.il/WebService/Rss/RssFeeder.asmx/FeederKeyword?iID=1388'), ] diff --git a/recipes/endgadget.recipe b/recipes/endgadget.recipe index 451df50f26..df10afd02a 100644 --- a/recipes/endgadget.recipe +++ b/recipes/endgadget.recipe @@ -87,8 +87,8 @@ class Engadget(BasicNewsRecipe): except KeyError: continue # Reorder the "title" and "content" elements - title_div = soup.find("div", {"class": "caas-title-wrapper"}) - content_div = soup.find("div", {"class": "caas-content-wrapper"}) + title_div = soup.find('div', {'class': 'caas-title-wrapper'}) + content_div = soup.find('div', {'class': 'caas-content-wrapper'}) if title_div and content_div: soup.body.clear() soup.body.append(title_div) diff --git a/recipes/equestria_daily.recipe b/recipes/equestria_daily.recipe index b0d15db6a9..63d8b48d84 100644 --- a/recipes/equestria_daily.recipe +++ b/recipes/equestria_daily.recipe @@ -5,12 +5,12 @@ from calibre.web.feeds.news import BasicNewsRecipe class AdvancedUserRecipe1639926896(BasicNewsRecipe): - __author__ = "Aisteru" - __copyright__ = "2021, Timothée Andres " + __author__ = 'Aisteru' + __copyright__ = '2021, Timothée Andres ' __license__ = 'GNU General Public License v3 - http://www.gnu.org/copyleft/gpl.html' - title = "Equestria Daily" - description = "Everything new in Equestria and beyond!" + title = 'Equestria Daily' + description = 'Everything new in Equestria and beyond!' language = 'en' # Max. supported by website: 50 @@ -29,13 +29,13 @@ class AdvancedUserRecipe1639926896(BasicNewsRecipe): # To discard posts under a certain section, simply comment the whole line sections = [ - ("Art", 'Art'), - ("News", 'News'), - ("Fics", 'Fanfiction'), - ("Media", 'Media'), - ("Comics", 'Comic'), - ("Community", 'Community'), - ("Editorial", 'Editorial'), + ('Art', 'Art'), + ('News', 'News'), + ('Fics', 'Fanfiction'), + ('Media', 'Media'), + ('Comics', 'Comic'), + ('Community', 'Community'), + ('Editorial', 'Editorial'), ] def get_masthead_url(self): diff --git a/recipes/expansion_spanish.recipe b/recipes/expansion_spanish.recipe index 812254c6c0..783bcffc49 100644 --- a/recipes/expansion_spanish.recipe +++ b/recipes/expansion_spanish.recipe @@ -106,15 +106,15 @@ class expansion_spanish(BasicNewsRecipe): cover = None st = time.localtime() year = str(st.tm_year) - month = "%.2d" % st.tm_mon - day = "%.2d" % st.tm_mday + month = '%.2d' % st.tm_mon + day = '%.2d' % st.tm_mday cover = 'http://img5.kiosko.net/' + year + '/' + \ month + '/' + day + '/es/expansion.750.jpg' br = BasicNewsRecipe.get_browser(self) try: br.open(cover) except: - self.log("\nPortada no disponible") + self.log('\nPortada no disponible') cover = 'http://www.aproahp.org/enlaces/images/diario_expansion.gif' return cover @@ -138,13 +138,13 @@ class expansion_spanish(BasicNewsRecipe): link = article.get('link', None) if link is None: return article - if link.split('/')[-1] == "story01.htm": + if link.split('/')[-1] == 'story01.htm': link = link.split('/')[-2] a = ['0B', '0C', '0D', '0E', '0F', '0G', '0N', '0L0S', '0A'] b = ['.', '/', '?', '-', '=', '&', '.com', 'www.', '0'] for i in range(0, len(a)): link = link.replace(a[i], b[i]) - link = "http://" + link + link = 'http://' + link # Eliminar artículos duplicados en otros feeds diff --git a/recipes/fastcompany.recipe b/recipes/fastcompany.recipe index c867c7b8ae..aeb295b973 100644 --- a/recipes/fastcompany.recipe +++ b/recipes/fastcompany.recipe @@ -51,9 +51,9 @@ class FastCompany(BasicNewsRecipe): soup.html['xml:lang'] = self.lang soup.html['lang'] = self.lang mlang = new_tag(soup, 'meta', [ - ("http-equiv", "Content-Language"), ("content", self.lang)]) + ('http-equiv', 'Content-Language'), ('content', self.lang)]) mcharset = new_tag(soup, 'meta', [ - ("http-equiv", "Content-Type"), ("content", "text/html; charset=UTF-8")]) + ('http-equiv', 'Content-Type'), ('content', 'text/html; charset=UTF-8')]) soup.head.insert(0, mlang) soup.head.insert(1, mcharset) for item in soup.findAll('a'): diff --git a/recipes/faz_net.recipe b/recipes/faz_net.recipe index 90b789a2c4..093b6b320b 100644 --- a/recipes/faz_net.recipe +++ b/recipes/faz_net.recipe @@ -21,7 +21,7 @@ def format_tickaroo_liveblog(soup): #format liveblogs for tag in soup.findAll('time'): - ntag = soup.new_tag("br") + ntag = soup.new_tag('br') tag.insert_before(ntag) for tag in soup.findAll(class_ = 'tik4-author__wrapper'): @@ -61,14 +61,14 @@ def bilderstrecke(soup,tag): # head.append(struct[i-1]) cap = soup.new_tag('p') cap.append(struct[int(v['caption'])]) - cap['class'] = "body-elements__image-figcaption" + cap['class'] = 'body-elements__image-figcaption' if 'source' in v.keys(): cred = soup.new_tag('span') cred.append(struct[int(v['source'])]) - cred['class'] = "body-elements__image-figcaption--source" + cred['class'] = 'body-elements__image-figcaption--source' cap.append(cred) if 'defaultUrl' in v.keys(): - fig = soup.new_tag("figure") + fig = soup.new_tag('figure') img = soup.new_tag('img') img['src'] = struct[int(v['defaultUrl'])] fig.append(img) @@ -145,7 +145,7 @@ class FazNet(BasicNewsRecipe): 'tik4-by','header-detail__image','mm-adbox','upper-toolbar content-container' ]}), # dict(name ='script'), - dict(name = "style"), + dict(name = 'style'), dict(name='svg'), dict(name='div', attrs={'data-module':'teaser'}), @@ -215,9 +215,9 @@ class FazNet(BasicNewsRecipe): for par in soup.findAll('p'): if len(par.contents) == 1: cont = str(par.contents[0]) - if re.search(r"^[1-9]\d* Bilder$",cont): + if re.search(r'^[1-9]\d* Bilder$',cont): # print(cont) - for tag in soup.findAll('script',attrs={'id':"__NUXT_DATA__",'type':'application/json'}): + for tag in soup.findAll('script',attrs={'id':'__NUXT_DATA__','type':'application/json'}): bilderstrecke(soup,tag) break break @@ -227,14 +227,14 @@ class FazNet(BasicNewsRecipe): tag.unwrap() # remove ":"" - tag = soup.find(class_ ="header-label__content") + tag = soup.find(class_ ='header-label__content') if tag: - colon=tag.find(class_ ="sr-only") + colon=tag.find(class_ ='sr-only') if colon: colon.extract() # Skip articles behind paywall - if soup.find(id = "faz-paywall"): + if soup.find(id = 'faz-paywall'): self.abort_article('Skipping paywalled article') # Remove F.A.Z. ad @@ -271,5 +271,5 @@ class FazNet(BasicNewsRecipe): text = str(tag.string) text = text.strip() if text != '' and text[-1] not in ['.','?','!',':']: - tag.string.replace_with(text + ".") + tag.string.replace_with(text + '.') return self.adeify_images(soup) diff --git a/recipes/financial_times.recipe b/recipes/financial_times.recipe index 337f6ea949..5df77f095d 100644 --- a/recipes/financial_times.recipe +++ b/recipes/financial_times.recipe @@ -13,7 +13,7 @@ from calibre.web.feeds.news import BasicNewsRecipe, classes class ft(BasicNewsRecipe): title = 'Financial Times' language = 'en' - __author__ = "Kovid Goyal" + __author__ = 'Kovid Goyal' description = 'The Financial Times is one of the world’s leading news organisations, recognised internationally for its authority, integrity and accuracy.' oldest_article = 1.15 max_articles_per_feed = 50 diff --git a/recipes/financialsense.recipe b/recipes/financialsense.recipe index fdf45c45b8..6bc1bc23aa 100644 --- a/recipes/financialsense.recipe +++ b/recipes/financialsense.recipe @@ -22,12 +22,12 @@ class FinancialSense(BasicNewsRecipe): remove_empty_feeds = True publication_type = 'newsportal' masthead_url = 'http://www.financialsense.com/sites/default/files/logo.jpg' - extra_css = """ + extra_css = ''' body{font-family: Arial,"Helvetica Neue",Helvetica,sans-serif } img{margin-bottom: 0.4em; display:block} h2{color: gray} .name{margin-right: 5em} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/first_things.recipe b/recipes/first_things.recipe index c044afa125..17d6a18eb2 100644 --- a/recipes/first_things.recipe +++ b/recipes/first_things.recipe @@ -16,7 +16,7 @@ class FirstThings(BasicNewsRecipe): title = 'First Things' __author__ = 'John Hutson' - description = 'America\'s Most Influential Journal of Religion and Public Life' + description = "America's Most Influential Journal of Religion and Public Life" INDEX = 'https://www.firstthings.com/current-edition' language = 'en' encoding = 'utf-8' diff --git a/recipes/flickr.recipe b/recipes/flickr.recipe index 63a1b9e66f..474648932e 100644 --- a/recipes/flickr.recipe +++ b/recipes/flickr.recipe @@ -31,13 +31,13 @@ class AdvancedUserRecipe1297031650(BasicNewsRecipe): remove_javascript = True language = 'en' - extra_css = """ + extra_css = ''' p{text-align: justify; font-size: 100%} body{ text-align: left; font-size:100% } h2{font-family: sans-serif; font-size:130%; font-weight:bold; text-align: justify; } .published{font-family:Arial,Helvetica,sans-serif; font-size:80%; } .posted{font-family:Arial,Helvetica,sans-serif; font-size:80%; } - """ + ''' keep_only_tags = [ dict(name='div', attrs={'class': 'entry'}) diff --git a/recipes/flickr_es.recipe b/recipes/flickr_es.recipe index 377e7f154a..b6049c7b2a 100644 --- a/recipes/flickr_es.recipe +++ b/recipes/flickr_es.recipe @@ -31,13 +31,13 @@ class AdvancedUserRecipe1297031650(BasicNewsRecipe): remove_javascript = True language = 'es' - extra_css = """ + extra_css = ''' p{text-align: justify; font-size: 100%} body{ text-align: left; font-size:100% } h2{font-family: sans-serif; font-size:130%; font-weight:bold; text-align: justify; } .published{font-family:Arial,Helvetica,sans-serif; font-size:80%; } .posted{font-family:Arial,Helvetica,sans-serif; font-size:80%; } - """ + ''' keep_only_tags = [ dict(name='div', attrs={'class': 'entry'}) diff --git a/recipes/fokus.recipe b/recipes/fokus.recipe index 20321d5efe..2bcb22ba19 100644 --- a/recipes/fokus.recipe +++ b/recipes/fokus.recipe @@ -160,7 +160,7 @@ class Fokus(BasicNewsRecipe): """ def _log(article) -> None: - """Log a digestible summary of the input `article` blurb.""" + '''Log a digestible summary of the input `article` blurb.''' log_message = f"\t{article['title']} : {article['date']} : {article['url']}" if article.get('description'): log_message += f" : {article['description']}" @@ -224,7 +224,7 @@ class Fokus(BasicNewsRecipe): sections: dict[str, str], articles: dict[str, dict[str, str, str, str]], ) -> dict[str, list[dict[str, str, str, str]]]: - """Assign each article in `articles` to a section in `sections`. + '''Assign each article in `articles` to a section in `sections`. Args: sections (dict[str, str]): A dict of section URLs as keys and section titles as values. @@ -232,7 +232,7 @@ class Fokus(BasicNewsRecipe): Returns: dict[str, list[dict[str, str, str, str]]]: A dict on a `{section_title: list[article_dict]}` format. - """ + ''' self.log(f'Assigning each of the {len(articles)} articles to either of the {len(sections)} sections...') section_to_articles = {} for article_url, article_dict in articles.items(): diff --git a/recipes/folha.recipe b/recipes/folha.recipe index 3bc31aee0e..91a9371c28 100644 --- a/recipes/folha.recipe +++ b/recipes/folha.recipe @@ -28,10 +28,10 @@ class Folha_de_s_paulo(BasicNewsRecipe): remove_empty_feeds = True publication_type = 'newspaper' masthead_url = 'http://f.i.uol.com.br/fsp/furniture/images/lgo-fsp-430x50-ffffff.gif' - extra_css = """ + extra_css = ''' body{font-family: Arial,Helvetica,sans-serif } img{margin-bottom: 0.4em; display:block} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/folhadesaopaulo_sub.recipe b/recipes/folhadesaopaulo_sub.recipe index 0b1d263cca..e9388030a2 100644 --- a/recipes/folhadesaopaulo_sub.recipe +++ b/recipes/folhadesaopaulo_sub.recipe @@ -49,7 +49,7 @@ class FSP(BasicNewsRecipe): # this solves the problem with truncated content in Kindle conversion_options = {'linearize_tables': True} - extra_css = """ + extra_css = ''' #articleNew { font: 18px Times New Roman,verdana,arial; } img { background: none !important; float: none; margin: 0px; } .newstexts { list-style-type: none; height: 20px; margin: 15px 0 10px 0; } @@ -82,14 +82,14 @@ img { background: none !important; float: none; margin: 0px; } .divisor { text-indent: -9999px; border-bottom: 1px solid #ccc; height: 1px; margin: 0; } .star { background: none !important; height: 15px; } .articleGraphic { margin-bottom: 20px; } -""" +''' # This is the code for login, here a mini browser is called and id entered def get_browser(self): br = BasicNewsRecipe.get_browser(self) if self.username is not None and self.password is not None: br.open('https://login.folha.com.br/login') - br.select_form(action="https://login.folha.com.br/login") + br.select_form(action='https://login.folha.com.br/login') br['email'] = self.username br['password'] = self.password br.submit() diff --git a/recipes/foreign_policy.recipe b/recipes/foreign_policy.recipe index 89d8fc5b2d..10b969c756 100644 --- a/recipes/foreign_policy.recipe +++ b/recipes/foreign_policy.recipe @@ -85,7 +85,7 @@ class ForeignPolicy(BasicNewsRecipe): if dek: desc += ' | ' + self.tag_to_string(dek) self.log('\t', title, url, '\n\t', desc) - feeds_dict[current_section].append({"title": title, "url": url, "description": desc}) + feeds_dict[current_section].append({'title': title, 'url': url, 'description': desc}) return [(section, articles) for section, articles in feeds_dict.items()] def preprocess_html(self, soup): diff --git a/recipes/foreignaffairs.recipe b/recipes/foreignaffairs.recipe index 4ca3d684f5..3d049036a8 100644 --- a/recipes/foreignaffairs.recipe +++ b/recipes/foreignaffairs.recipe @@ -37,20 +37,20 @@ def get_issue_data(br, log, node_id='1126213', year='2020', volnum='99', issue_v return { 'from': 0, 'post_filter': {'bool': q}, - "_source": { - "includes": [ - "nid", 'path', 'title', 'field_subtitle', 'field_display_authors', + '_source': { + 'includes': [ + 'nid', 'path', 'title', 'field_subtitle', 'field_display_authors', 'fa_node_type_or_subtype', 'field_issue_sspecial_articles__nid', 'field_issue_sspecial_header' ] }, - "query": { - "match_all": {} + 'query': { + 'match_all': {} }, - 'sort': [{'field_sequence': "asc"}, {'fa_normalized_date': "desc"}], - "size": size, + 'sort': [{'field_sequence': 'asc'}, {'fa_normalized_date': 'desc'}], + 'size': size, } def get_data(data): @@ -171,9 +171,9 @@ class ForeignAffairsRecipe(BasicNewsRecipe): cov = main.find('img', attrs={'srcset': lambda x: x and 'Cover.jpg' in x}) if cov: self.cover_url = re.sub( - r"_webp_issue_small_\dx", - "_webp_issue_large_2x", - cov["srcset"].split()[0] + r'_webp_issue_small_\dx', + '_webp_issue_large_2x', + cov['srcset'].split()[0] ) cls = soup.find('link', attrs={'rel':'shortlink'})['href'] @@ -188,7 +188,7 @@ class ForeignAffairsRecipe(BasicNewsRecipe): for by in soup.findAll(**classes('topper__byline topper__date font-style-italic')): by.name = 'div' for img in soup.find_all('img', attrs={'srcset': True}): - img['src'] = re.sub(r"_webp_small_\dx", "_webp_large_1x",img['srcset'].split()[0]) + img['src'] = re.sub(r'_webp_small_\dx', '_webp_large_1x',img['srcset'].split()[0]) return soup def get_browser(self): diff --git a/recipes/foxnews.recipe b/recipes/foxnews.recipe index 402b2ea894..4949049fd2 100644 --- a/recipes/foxnews.recipe +++ b/recipes/foxnews.recipe @@ -25,11 +25,11 @@ class FoxNews(BasicNewsRecipe): language = 'en_US' remove_empty_feeds = True - extra_css = """ + extra_css = ''' body{font-family: Arial,sans-serif } .caption{font-size: x-small} .author,.dateline{font-size: small} - """ + ''' recipe_specific_options = { 'days': { diff --git a/recipes/free_inquiry.recipe b/recipes/free_inquiry.recipe index ced2c2f160..882b8f5c67 100644 --- a/recipes/free_inquiry.recipe +++ b/recipes/free_inquiry.recipe @@ -25,14 +25,14 @@ class FreeInquiry(BasicNewsRecipe): ignore_duplicate_articles = {'url'} remove_empty_feeds = True needs_subscription = True - extra_css = """ + extra_css = ''' .entry-header{ text-transform: uppercase; vertical-align: baseline; display: inline; } ul li{display: inline} - """ + ''' remove_tags = [ classes( diff --git a/recipes/frontline.recipe b/recipes/frontline.recipe index f1a4d8af36..7a435ac732 100644 --- a/recipes/frontline.recipe +++ b/recipes/frontline.recipe @@ -95,5 +95,5 @@ class Frontline(BasicNewsRecipe): if not url or not title: continue self.log(section, '\n\t', title, '\n\t', desc, '\n\t\t', url) - feeds_dict[section].append({"title": title, "url": url, "description": desc}) + feeds_dict[section].append({'title': title, 'url': url, 'description': desc}) return [(section, articles) for section, articles in feeds_dict.items()] diff --git a/recipes/galaxys_edge.recipe b/recipes/galaxys_edge.recipe index 233277a29c..8d42d4abef 100644 --- a/recipes/galaxys_edge.recipe +++ b/recipes/galaxys_edge.recipe @@ -34,18 +34,18 @@ class AdvancedUserRecipe1515196393(BasicNewsRecipe): issue_title = soup.find('h1') self.title = "Galaxy's Edge: " + self.tag_to_string(issue_title).lower().title() toc = soup.find('div', attrs={'class':'nav-tabs'}) - current_section = "Articles" + current_section = 'Articles' current_articles = [] feeds = [] br = self.get_browser() self.ctdir = PersistentTemporaryDirectory() - for x in toc.findAll(['li'], attrs={"class": re.compile(".*get_content.*")}): + for x in toc.findAll(['li'], attrs={'class': re.compile('.*get_content.*')}): edwo = x.find('a') title = self.tag_to_string(edwo) self.log('\t\tFound article:', title) - post_id = x["data-post-id"] - cat_id = x["data-cat-id"] - parent_id = x["data-parent-id"] + post_id = x['data-post-id'] + cat_id = x['data-cat-id'] + parent_id = x['data-parent-id'] self.log('\t\tdata-parent-id', parent_id) self.log('\t\tdata-cat-id', cat_id) self.log('\t\tdata-post-id', post_id) @@ -61,5 +61,5 @@ class AdvancedUserRecipe1515196393(BasicNewsRecipe): return feeds def cleanup(self): - self.log("Deleting temp files...") + self.log('Deleting temp files...') shutil.rmtree(self.ctdir) diff --git a/recipes/gazeta-prawna-calibre-v1.recipe b/recipes/gazeta-prawna-calibre-v1.recipe index e78a9f1861..71cca67d83 100644 --- a/recipes/gazeta-prawna-calibre-v1.recipe +++ b/recipes/gazeta-prawna-calibre-v1.recipe @@ -69,8 +69,8 @@ class gazetaprawna(BasicNewsRecipe): parsed_feeds = BasicNewsRecipe.parse_feeds(self) for n, feed in enumerate(parsed_feeds): for a, article in enumerate(feed): - article.text_summary = re.sub(r'<\!\[CDATA\[', "", article.text_summary) - article.text_summary = re.sub(r'\]\]', "", article.text_summary) + article.text_summary = re.sub(r'<\!\[CDATA\[', '', article.text_summary) + article.text_summary = re.sub(r'\]\]', '', article.text_summary) article.summary = article.text_summary return parsed_feeds @@ -84,10 +84,10 @@ class gazetaprawna(BasicNewsRecipe): for span in soup.findAll(name='span'): if len(self.tag_to_string(span)) > 1: - span.append(" ") + span.append(' ') for locked in soup.findAll(name='div', attrs={'class': ['articleGate']}): - locked.append(u"Przejd\u017a do artyku\u0142u na GazetaPrawna.pl aby zalogowa\u0107 si\u0119 lub wykupi\u0107 dost\u0119p") + locked.append(u'Przejd\u017a do artyku\u0142u na GazetaPrawna.pl aby zalogowa\u0107 si\u0119 lub wykupi\u0107 dost\u0119p') return soup @@ -108,5 +108,5 @@ class gazetaprawna(BasicNewsRecipe): def get_cover_url(self): soup = self.index_to_soup( 'http://www.egazety.pl/infor/e-wydanie-dziennik-gazeta-prawna.html') - self.cover_url = soup.find("a", {"class": "image cover-preview"}).img['src'] + self.cover_url = soup.find('a', {'class': 'image cover-preview'}).img['src'] return getattr(self, 'cover_url', self.cover_url) diff --git a/recipes/globes_co_il.recipe b/recipes/globes_co_il.recipe index 627801bce6..8102642749 100644 --- a/recipes/globes_co_il.recipe +++ b/recipes/globes_co_il.recipe @@ -21,21 +21,21 @@ class AdvancedUserRecipe1283848012(BasicNewsRecipe): remove_attributes = ['width', 'style'] feeds = [ - (u"עידכוני RSS ", u"https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=3038"), - (u"כל הכתבות", u"https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=2"), - (u"שוק ההון", u"https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=585"), - (u"בארץ", u"https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=9917"), - (u"גלובלי ושוקי עולם", u"https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=1225"), - (u"גלובסטק", u"https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=594"), - (u"דין וחשבון", u"https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=829"), - (u"דעות", u"https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=845"), - (u"וידאו", u"https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=2007"), - (u"ליידי גלובס", u"https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=3314"), - (u"מגזין G", u"https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=3312"), - (u"nadlan", u"https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=607"), - (u"נתח שוק וצרכנות", u"https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=821"), - (u"מטבעות דיגיטליים", u"https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=9758"), - (u"קריירה", u"https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iid=3266"), - (u"תיירות", u"https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iid=9010"), - (u"רכב", u"https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=3220") + (u'עידכוני RSS ', u'https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=3038'), + (u'כל הכתבות', u'https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=2'), + (u'שוק ההון', u'https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=585'), + (u'בארץ', u'https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=9917'), + (u'גלובלי ושוקי עולם', u'https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=1225'), + (u'גלובסטק', u'https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=594'), + (u'דין וחשבון', u'https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=829'), + (u'דעות', u'https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=845'), + (u'וידאו', u'https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=2007'), + (u'ליידי גלובס', u'https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=3314'), + (u'מגזין G', u'https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=3312'), + (u'nadlan', u'https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=607'), + (u'נתח שוק וצרכנות', u'https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=821'), + (u'מטבעות דיגיטליים', u'https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=9758'), + (u'קריירה', u'https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iid=3266'), + (u'תיירות', u'https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iid=9010'), + (u'רכב', u'https://www.globes.co.il/webservice/rss/rssfeeder.asmx/FeederNode?iID=3220') ] diff --git a/recipes/go_comics.recipe b/recipes/go_comics.recipe index 57c23d87e1..fbd79f714a 100644 --- a/recipes/go_comics.recipe +++ b/recipes/go_comics.recipe @@ -61,7 +61,7 @@ class GoComics(BasicNewsRecipe): # (u"Andertoons",u"http://www.gocomics.com/andertoons"), # (u"Andy Capp",u"http://www.gocomics.com/andycapp"), # (u"Angry Little Girls",u"http://www.gocomics.com/angry-little-girls"), - (u"Animal Crackers", u"http://www.gocomics.com/animalcrackers"), + (u'Animal Crackers', u'http://www.gocomics.com/animalcrackers'), # (u"Annie",u"http://www.gocomics.com/annie"), # (u"The Argyle Sweater",u"http://www.gocomics.com/theargylesweater"), # (u"Robert Ariail",u"http://www.gocomics.com/robert-ariail"), @@ -71,14 +71,14 @@ class GoComics(BasicNewsRecipe): # (u"At the Zoo",u"http://www.gocomics.com/at-the-zoo"), # (u"Aunty Acid",u"http://www.gocomics.com/aunty-acid"), # (u"The Awkward Yeti",u"http://www.gocomics.com/the-awkward-yeti"), - (u"B.C.",u"http://www.gocomics.com/bc"), + (u'B.C.',u'http://www.gocomics.com/bc'), # (u"Back to B.C.",u"http://www.gocomics.com/back-to-bc"), # (u"Back in the Day",u"http://www.gocomics.com/backintheday"), # (u"bacon",u"http://www.gocomics.com/bacon"), # (u"Bad Machinery",u"http://www.gocomics.com/bad-machinery"), # (u"Bad Reporter",u"http://www.gocomics.com/badreporter"), # (u"Badlands",u"http://www.gocomics.com/badlands"), - (u"Baldo",u"http://www.gocomics.com/baldo"), + (u'Baldo',u'http://www.gocomics.com/baldo'), # (u"Ballard Street",u"http://www.gocomics.com/ballardstreet"), # (u"Banana Triangle",u"http://www.gocomics.com/banana-triangle"), # (u"Barkeater Lake Pandolph",u"http://www.gocomics.com/barkeaterlake"), @@ -115,10 +115,10 @@ class GoComics(BasicNewsRecipe): # (u"Chip Bok",u"http://www.gocomics.com/chipbok"), # (u"Boomerangs",u"http://www.gocomics.com/boomerangs"), # (u"The Boondocks",u"http://www.gocomics.com/boondocks"), - (u"The Born Loser",u"http://www.gocomics.com/the-born-loser"), + (u'The Born Loser',u'http://www.gocomics.com/the-born-loser'), # (u"Matt Bors",u"http://www.gocomics.com/matt-bors"), # (u"Bottomliners",u"http://www.gocomics.com/bottomliners"), - (u"Bound and Gagged",u"http://www.gocomics.com/boundandgagged"), + (u'Bound and Gagged',u'http://www.gocomics.com/boundandgagged'), # (u"Brain Squirts",u"http://www.gocomics.com/brain-squirts"), # (u"Break of Day",u"http://www.gocomics.com/break-of-day"), # (u"Breaking Cat News",u"http://www.gocomics.com/breaking-cat-news"), @@ -126,12 +126,12 @@ class GoComics(BasicNewsRecipe): # (u"Brevity",u"http://www.gocomics.com/brevitypanel"), # (u"Brewster Rockit",u"http://www.gocomics.com/brewsterrockit"), # (u"Chris Britt",u"http://www.gocomics.com/chrisbritt"), - (u"Broom Hilda",u"http://www.gocomics.com/broomhilda"), + (u'Broom Hilda',u'http://www.gocomics.com/broomhilda'), # (u"The Buckets",u"http://www.gocomics.com/thebuckets"), # (u"Bully",u"http://www.gocomics.com/bully"), # (u"Buni",u"http://www.gocomics.com/buni"), # (u"Bushy Tales",u"http://www.gocomics.com/bushy-tales"), - (u"Calvin and Hobbes",u"http://www.gocomics.com/calvinandhobbes"), + (u'Calvin and Hobbes',u'http://www.gocomics.com/calvinandhobbes'), # (u"Candorville",u"http://www.gocomics.com/candorville"), # (u"Stuart Carlson",u"http://www.gocomics.com/stuartcarlson"), # (u"Ken Catalino",u"http://www.gocomics.com/kencatalino"), @@ -202,7 +202,7 @@ class GoComics(BasicNewsRecipe): # (u"Flo and Friends",u"http://www.gocomics.com/floandfriends"), # (u"The Flying McCoys",u"http://www.gocomics.com/theflyingmccoys"), # (u"Foolish Mortals",u"http://www.gocomics.com/foolish-mortals"), - (u"For Better or For Worse",u"http://www.gocomics.com/forbetterorforworse"), + (u'For Better or For Worse',u'http://www.gocomics.com/forbetterorforworse'), # (u"For Heaven's Sake",u"http://www.gocomics.com/forheavenssake"), # (u"Fort Knox",u"http://www.gocomics.com/fortknox"), # (u"Four Eyes",u"http://www.gocomics.com/four-eyes"), @@ -210,7 +210,7 @@ class GoComics(BasicNewsRecipe): # (u"FoxTrot",u"http://www.gocomics.com/foxtrot"), # (u"FoxTrot Classics",u"http://www.gocomics.com/foxtrotclassics"), # (u"Francis",u"http://www.gocomics.com/francis"), - (u"Frank and Ernest",u"http://www.gocomics.com/frank-and-ernest"), + (u'Frank and Ernest',u'http://www.gocomics.com/frank-and-ernest'), # (u"Frankie Comics",u"http://www.gocomics.com/frankie-comics"), # (u"Frazz",u"http://www.gocomics.com/frazz"), # (u"Fred Basset",u"http://www.gocomics.com/fredbasset"), @@ -219,7 +219,7 @@ class GoComics(BasicNewsRecipe): # (u"Frog Applause",u"http://www.gocomics.com/frogapplause"), # (u"From the Mo Willems Sketchbook",u"http://www.gocomics.com/from-the-mo-willems-sketchbook"), # (u"The Fusco Brothers",u"http://www.gocomics.com/thefuscobrothers"), - (u"Garfield",u"http://www.gocomics.com/garfield"), + (u'Garfield',u'http://www.gocomics.com/garfield'), # (u"Garfield Classics",u"http://www.gocomics.com/garfield-classics"), # (u"Garfield Minus Garfield",u"http://www.gocomics.com/garfieldminusgarfield"), # (u"Gasoline Alley",u"http://www.gocomics.com/gasolinealley"), @@ -227,7 +227,7 @@ class GoComics(BasicNewsRecipe): # (u"Gentle Creatures",u"http://www.gocomics.com/gentle-creatures"), # (u"The Gentleman's Armchair",u"http://www.gocomics.com/the-gentlemans-armchair"), # (u"Get a Life",u"http://www.gocomics.com/getalife"), - (u"Get Fuzzy",u"http://www.gocomics.com/getfuzzy"), + (u'Get Fuzzy',u'http://www.gocomics.com/getfuzzy'), # (u"Gil",u"http://www.gocomics.com/gil"), # (u"Gil Thorp",u"http://www.gocomics.com/gilthorp"), # (u"Ginger Meggs",u"http://www.gocomics.com/gingermeggs"), @@ -248,7 +248,7 @@ class GoComics(BasicNewsRecipe): # (u"Phil Hands",u"http://www.gocomics.com/phil-hands"), # (u"Health Capsules",u"http://www.gocomics.com/healthcapsules"), # (u"Heart of the City",u"http://www.gocomics.com/heartofthecity"), - (u"Heathcliff",u"http://www.gocomics.com/heathcliff"), + (u'Heathcliff',u'http://www.gocomics.com/heathcliff'), # (u"Joe Heller",u"http://www.gocomics.com/joe-heller"), # (u"Rebecca Hendin",u"http://www.gocomics.com/rebecca-hendin"), # (u"Herb and Jamaal",u"http://www.gocomics.com/herbandjamaal"), @@ -313,7 +313,7 @@ class GoComics(BasicNewsRecipe): # (u"Lost Side of Suburbia",u"http://www.gocomics.com/lostsideofsuburbia"), # (u"Lost Sheep",u"http://www.gocomics.com/lostsheep"), # (u"Chan Lowe",u"http://www.gocomics.com/chanlowe"), - (u"Luann",u"http://www.gocomics.com/luann"), + (u'Luann',u'http://www.gocomics.com/luann'), # (u"Luann Againn",u"http://www.gocomics.com/luann-againn"), # (u"Mike Luckovich",u"http://www.gocomics.com/mikeluckovich"), # (u"Lucky Cow",u"http://www.gocomics.com/luckycow"), @@ -326,7 +326,7 @@ class GoComics(BasicNewsRecipe): # (u"Making It",u"http://www.gocomics.com/making-it"), # (u"Maria's Day",u"http://www.gocomics.com/marias-day"), # (u"Gary Markstein",u"http://www.gocomics.com/garymarkstein"), - (u"Marmaduke",u"http://www.gocomics.com/marmaduke"), + (u'Marmaduke',u'http://www.gocomics.com/marmaduke'), # (u"The Martian Confederacy",u"http://www.gocomics.com/the-martian-confederacy"), # (u"MazeToons Puzzle",u"http://www.gocomics.com/mazetoons-puzzle"), # (u"Glenn McCoy",u"http://www.gocomics.com/glennmccoy"), @@ -335,13 +335,13 @@ class GoComics(BasicNewsRecipe): # (u"Medium Large",u"http://www.gocomics.com/medium-large"), # (u"Meg Classics",u"http://www.gocomics.com/meg-classics"), # (u"Microcosm",u"http://www.gocomics.com/microcosm"), - (u"The Middletons",u"http://www.gocomics.com/themiddletons"), + (u'The Middletons',u'http://www.gocomics.com/themiddletons'), # (u"Mike du Jour",u"http://www.gocomics.com/mike-du-jour"), # (u"Minimum Security",u"http://www.gocomics.com/minimumsecurity"), # (u"Moderately Confused",u"http://www.gocomics.com/moderately-confused"), # (u"Molebashed",u"http://www.gocomics.com/molebashed"), # (u"Molly and the Bear",u"http://www.gocomics.com/mollyandthebear"), - (u"Momma",u"http://www.gocomics.com/momma"), + (u'Momma',u'http://www.gocomics.com/momma'), # (u"Mom's Cancer",u"http://www.gocomics.com/moms-cancer"), # (u"Monty",u"http://www.gocomics.com/monty"), # (u"Jim Morin",u"http://www.gocomics.com/jimmorin"), @@ -359,7 +359,7 @@ class GoComics(BasicNewsRecipe): # (u"New Adventures of Queen Victoria",u"http://www.gocomics.com/thenewadventuresofqueenvictoria"), # (u"Next Door Neighbors",u"http://www.gocomics.com/next-door-neighbors"), # (u"Nick and Zuzu",u"http://www.gocomics.com/nick-and-zuzu), - (u"Non Sequitur",u"http://www.gocomics.com/nonsequitur"), + (u'Non Sequitur',u'http://www.gocomics.com/nonsequitur'), # (u"The Norm 4.0",u"http://www.gocomics.com/the-norm-4-0"), # (u"The Norm Classics",u"http://www.gocomics.com/thenorm"), # (u"Not Invented Here",u"http://www.gocomics.com/not-invented-here"), @@ -383,10 +383,10 @@ class GoComics(BasicNewsRecipe): # (u"Ozy and Millie",u"http://www.gocomics.com/ozy-and-millie"), # (u"Henry Payne",u"http://www.gocomics.com/henrypayne"), # (u"PC and Pixel",u"http://www.gocomics.com/pcandpixel"), - (u"Peanuts",u"http://www.gocomics.com/peanuts"), + (u'Peanuts',u'http://www.gocomics.com/peanuts'), # (u"Peanuts Begins",u"http://www.gocomics.com/peanuts-begins"), # (u"Peanuts Holiday Countdown",u"http://www.gocomics.com/peanuts-holiday-countdown"), - (u"Pearls Before Swine",u"http://www.gocomics.com/pearlsbeforeswine"), + (u'Pearls Before Swine',u'http://www.gocomics.com/pearlsbeforeswine'), # (u"Perry Bible Fellowship",u"http://www.gocomics.com/perry-bible-fellowship"), # (u"Joel Pett",u"http://www.gocomics.com/joelpett"), # (u"Phoebe and Her Unicorn",u"http://www.gocomics.com/phoebe-and-her-unicorn"), @@ -398,7 +398,7 @@ class GoComics(BasicNewsRecipe): # (u"Pinkerton",u"http://www.gocomics.com/pinkerton"), # (u"Please Listen to Me",u"http://www.gocomics.com/please-listen-to-me"), # (u"Pluggers",u"http://www.gocomics.com/pluggers"), - (u"Pooch Cafe",u"http://www.gocomics.com/poochcafe"), + (u'Pooch Cafe',u'http://www.gocomics.com/poochcafe'), # (u"Poorcraft",u"http://www.gocomics.com/poorcraft"), # (u"Poorly Drawn Lines",u"http://www.gocomics.com/poorly-drawn-lines"), # (u"Pop Culture Shock Therapy",u"http://www.gocomics.com/pop-culture-shock-therapy"), @@ -427,7 +427,7 @@ class GoComics(BasicNewsRecipe): # (u"Ripley's Believe It or Not",u"http://www.gocomics.com/ripleysbelieveitornot"), # (u"Robbie and Bobby",u"http://www.gocomics.com/robbie-and-bobby"), # (u"Rob Rogers",u"http://www.gocomics.com/robrogers"), - (u"Rose is Rose",u"http://www.gocomics.com/roseisrose"), + (u'Rose is Rose',u'http://www.gocomics.com/roseisrose'), # (u"Rubes",u"http://www.gocomics.com/rubes"), # (u"Rudy Park",u"http://www.gocomics.com/rudypark"), # (u"Sarah's Scribbles",u"http://www.gocomics.com/sarahs-scribbles"), @@ -438,7 +438,7 @@ class GoComics(BasicNewsRecipe): # (u"Sheldon",u"http://www.gocomics.com/sheldon"), # (u"Drew Sheneman",u"http://www.gocomics.com/drewsheneman"), # (u"Shirley and Son Classics",u"http://www.gocomics.com/shirley-and-son-classics"), - (u"Shoe",u"http://www.gocomics.com/shoe"), + (u'Shoe',u'http://www.gocomics.com/shoe'), # (u"Shoecabbage",u"http://www.gocomics.com/shoecabbage"), # (u"Shortcuts",u"http://www.gocomics.com/shortcuts"), # (u"Shutterbug Follies",u"http://www.gocomics.com/shutterbug-follies"), @@ -524,7 +524,7 @@ class GoComics(BasicNewsRecipe): # (u"Winston",u"http://www.gocomics.com/winston"), # (u"Wit of the World",u"http://www.gocomics.com/witoftheworld"), # (u"CartoonArts International",u"http://www.gocomics.com/witoftheworld"), - (u"Wizard of Id",u"http://www.gocomics.com/wizardofid"), + (u'Wizard of Id',u'http://www.gocomics.com/wizardofid'), # (u"Wizard of Id Classics",u"http://www.gocomics.com/wizard-of-id-classics"), # (u"Wondermark",u"http://www.gocomics.com/wondermark"), # (u"Working Daze",u"http://www.gocomics.com/working-daze"), diff --git a/recipes/google_news.recipe b/recipes/google_news.recipe index 7eca31974a..9777951626 100644 --- a/recipes/google_news.recipe +++ b/recipes/google_news.recipe @@ -68,7 +68,7 @@ class google_news_de(BasicNewsRecipe): # feel free to add, wipe out what you need ---- can be edit by user # def get_feeds(self): - url = "https://geolocation-db.com/json" + url = 'https://geolocation-db.com/json' data = self.index_to_soup(url, raw=True) data = json.loads(data) country_code = str(data['country_code']).lower() # for me this is de diff --git a/recipes/gosc_full.recipe b/recipes/gosc_full.recipe index 2f1752f5be..26d05d39eb 100644 --- a/recipes/gosc_full.recipe +++ b/recipes/gosc_full.recipe @@ -32,7 +32,7 @@ class GN(BasicNewsRecipe): page = doc.xpath( '//div[@class="search-result release-result"]/div[1]/div[1]/a/@href') - if time.strftime("%w") in ['3', '4']: + if time.strftime('%w') in ['3', '4']: return page[5] else: return page[4] diff --git a/recipes/granta.recipe b/recipes/granta.recipe index 910e0a2cf5..989d4d9700 100644 --- a/recipes/granta.recipe +++ b/recipes/granta.recipe @@ -138,14 +138,14 @@ Magnitude = { def text2num(s): - a = re.split(r"[\s-]+", s) + a = re.split(r'[\s-]+', s) n = 0 g = 0 for w in a: x = Small.get(w, None) if x is not None: g += x - elif w == "hundred" and g != 0: + elif w == 'hundred' and g != 0: g *= 100 else: x = Magnitude.get(w, None) @@ -195,7 +195,7 @@ class Granta(BasicNewsRecipe): if captcha_question is not None: captcha = str(solve_captcha(captcha_question)) - br.select_form(method="post", action="https://granta.com/") + br.select_form(method='post', action='https://granta.com/') br['username'] = self.username br['password'] = self.password br['capcha'] = captcha diff --git a/recipes/grantland.recipe b/recipes/grantland.recipe index c0d400e84f..ca08c6a03f 100644 --- a/recipes/grantland.recipe +++ b/recipes/grantland.recipe @@ -4,7 +4,7 @@ from calibre.web.feeds.news import BasicNewsRecipe class GrantLand(BasicNewsRecipe): - title = u"Grantland" + title = u'Grantland' description = 'Writings on Sports & Pop Culture' language = 'en' __author__ = 'barty on mobileread.com forum' @@ -51,7 +51,7 @@ class GrantLand(BasicNewsRecipe): self.log('Reading category:', cat_name) articles = [] - page = "%s/%s" % (self.INDEX, tag) + page = '%s/%s' % (self.INDEX, tag) soup = self.index_to_soup(page) main = soup.find('div', id='col-main') diff --git a/recipes/greensboro_news_and_record.recipe b/recipes/greensboro_news_and_record.recipe index 1056b97743..1e04fb8864 100644 --- a/recipes/greensboro_news_and_record.recipe +++ b/recipes/greensboro_news_and_record.recipe @@ -8,7 +8,7 @@ from calibre.web.feeds.news import BasicNewsRecipe class NewsandRecord(BasicNewsRecipe): title = u'Greensboro News & Record' - description = "News from Greensboro, North Carolina" + description = 'News from Greensboro, North Carolina' __author__ = 'Walt Anthony' publisher = 'News & Record and Landmark Media Enterprises, LLC' category = 'news, USA' diff --git a/recipes/guardian.recipe b/recipes/guardian.recipe index cf6df01729..9e05967e4a 100644 --- a/recipes/guardian.recipe +++ b/recipes/guardian.recipe @@ -22,10 +22,10 @@ class Guardian(BasicNewsRecipe): title = u'The Guardian and The Observer' is_observer = False - base_url = "https://www.theguardian.com/uk" + base_url = 'https://www.theguardian.com/uk' if date.today().weekday() == 6: is_observer = True - base_url = "https://www.theguardian.com/observer" + base_url = 'https://www.theguardian.com/observer' __author__ = 'Kovid Goyal' language = 'en_GB' @@ -64,7 +64,7 @@ class Guardian(BasicNewsRecipe): classes('content__article-body js-bottom-marker article-body-commercial-selector'), ] - extra_css = """ + extra_css = ''' img { max-width: 100% !important; max-height: 100% !important; @@ -78,7 +78,7 @@ class Guardian(BasicNewsRecipe): font-size: 0.5em; color: #6B6B6B; } - """ + ''' def get_browser(self, *a, **kw): # This site returns images in JPEG-XR format if the user agent is IE diff --git a/recipes/haaretz_en.recipe b/recipes/haaretz_en.recipe index 525194c5d4..951687c1f7 100644 --- a/recipes/haaretz_en.recipe +++ b/recipes/haaretz_en.recipe @@ -33,13 +33,13 @@ class Haaretz_en(BasicNewsRecipe): PREFIX = 'https://www.haaretz.com' LOGIN = 'https://services.haaretz.com/ms-sso/loginUrlEncoded' LOGOUT = 'https://services.haaretz.com/ms-sso/logout' - extra_css = """ + extra_css = ''' body{font-family: Merriweather, "Helvetica Neue", Helvetica, Arial, sans-serif } div.mx time{display: none} div.my time{display: none} div.mq time{display: none} div.mr time{display: none} - """ + ''' conversion_options = { 'comment': description, 'publisher': publisher, 'language': language diff --git a/recipes/hankyoreh21.recipe b/recipes/hankyoreh21.recipe index 3113df68e7..f89476c9f9 100644 --- a/recipes/hankyoreh21.recipe +++ b/recipes/hankyoreh21.recipe @@ -36,4 +36,4 @@ class Hankyoreh21(BasicNewsRecipe): def get_article_url(self, article): org_url = BasicNewsRecipe.get_article_url(self, article) - return "http://h21.hani.co.kr" + org_url if org_url[0] == '/' else org_url + return 'http://h21.hani.co.kr' + org_url if org_url[0] == '/' else org_url diff --git a/recipes/harpers.recipe b/recipes/harpers.recipe index ee83add22c..c372d55fdc 100644 --- a/recipes/harpers.recipe +++ b/recipes/harpers.recipe @@ -35,7 +35,7 @@ class Harpers(BasicNewsRecipe): remove_tags = [ classes('header-controls') ] - remove_attributes = ["style", "width", "height"] + remove_attributes = ['style', 'width', 'height'] extra_css = ''' img {display:block; margin:0 auto;} @@ -64,8 +64,8 @@ class Harpers(BasicNewsRecipe): } def parse_index(self): - issues_soup = self.index_to_soup("https://harpers.org/issues/") - a_ele = issues_soup.select_one("div.issue-card a") + issues_soup = self.index_to_soup('https://harpers.org/issues/') + a_ele = issues_soup.select_one('div.issue-card a') self.timefmt = ' [' + self.tag_to_string(a_ele.find(attrs={'class':'issue-title'})) + ']' url = a_ele['href'] diff --git a/recipes/hbr.recipe b/recipes/hbr.recipe index 4dcde07c4e..8b326a18af 100644 --- a/recipes/hbr.recipe +++ b/recipes/hbr.recipe @@ -12,28 +12,28 @@ from calibre.web.feeds.news import BasicNewsRecipe, classes class HBR(BasicNewsRecipe): - title = "Harvard Business Review" - __author__ = "unkn0wn, updated by ping" + title = 'Harvard Business Review' + __author__ = 'unkn0wn, updated by ping' description = ( - "Harvard Business Review is the leading destination for smart management thinking. " - "Through its flagship magazine, books, and digital content and tools published on HBR.org, " - "Harvard Business Review aims to provide professionals around the world with rigorous insights " - "and best practices to help lead themselves and their organizations more effectively and to " - "make a positive impact." + 'Harvard Business Review is the leading destination for smart management thinking. ' + 'Through its flagship magazine, books, and digital content and tools published on HBR.org, ' + 'Harvard Business Review aims to provide professionals around the world with rigorous insights ' + 'and best practices to help lead themselves and their organizations more effectively and to ' + 'make a positive impact.' ) - language = "en" - masthead_url = "https://hbr.org/resources/css/images/hbr_logo.svg" - publication_type = "magazine" - encoding = "utf-8" + language = 'en' + masthead_url = 'https://hbr.org/resources/css/images/hbr_logo.svg' + publication_type = 'magazine' + encoding = 'utf-8' remove_javascript = True no_stylesheets = True auto_cleanup = False compress_news_images = True - ignore_duplicate_articles = {"url"} - base_url = "https://hbr.org" + ignore_duplicate_articles = {'url'} + base_url = 'https://hbr.org' - remove_attributes = ["height", "width", "style"] - extra_css = """ + remove_attributes = ['height', 'width', 'style'] + extra_css = ''' h1.article-hed { font-size: x-large; margin-bottom: 0.4rem; } .article-dek { font-size: large; font-style: italic; margin-bottom: 1rem; } .article-byline { margin-top: 0.7rem; font-size: medium; font-style: normal; font-weight: bold; } @@ -50,35 +50,35 @@ class HBR(BasicNewsRecipe): padding-top: 0.5rem; font-style: italic; } - """ + ''' keep_only_tags = [ classes( - "headline-container article-dek-group pub-date hero-image-content " - "article-body standard-content" + 'headline-container article-dek-group pub-date hero-image-content ' + 'article-body standard-content' ), ] remove_tags = [ classes( - "left-rail--container translate-message follow-topic " - "newsletter-container by-prefix related-topics--common" + 'left-rail--container translate-message follow-topic ' + 'newsletter-container by-prefix related-topics--common' ), - dict(name=["article-sidebar"]), + dict(name=['article-sidebar']), ] def preprocess_raw_html(self, raw_html, article_url): soup = self.soup(raw_html) # break author byline out of list - byline_list = soup.find("ul", class_="article-byline-list") + byline_list = soup.find('ul', class_='article-byline-list') if byline_list: byline = byline_list.parent byline.append( - ", ".join( + ', '.join( [ self.tag_to_string(author) - for author in byline_list.find_all(class_="article-author") + for author in byline_list.find_all(class_='article-author') ] ) ) @@ -86,44 +86,44 @@ class HBR(BasicNewsRecipe): # Extract full article content content_ele = soup.find( - "content", + 'content', attrs={ - "data-index": True, - "data-page-year": True, - "data-page-month": True, - "data-page-seo-title": True, - "data-page-slug": True, + 'data-index': True, + 'data-page-year': True, + 'data-page-month': True, + 'data-page-seo-title': True, + 'data-page-slug': True, }, ) - endpoint_url = "https://hbr.org/api/article/piano/content?" + urlencode( + endpoint_url = 'https://hbr.org/api/article/piano/content?' + urlencode( { - "year": content_ele["data-page-year"], - "month": content_ele["data-page-month"], - "seotitle": content_ele["data-page-seo-title"], + 'year': content_ele['data-page-year'], + 'month': content_ele['data-page-month'], + 'seotitle': content_ele['data-page-seo-title'], } ) data = { - "contentKey": content_ele["data-index"], - "pageSlug": content_ele["data-page-slug"], + 'contentKey': content_ele['data-index'], + 'pageSlug': content_ele['data-page-slug'], } headers = { - "User-Agent": random_user_agent(), - "Pragma": "no-cache", - "Cache-Control": "no-cache", - "Content-Type": "application/json", - "Referer": article_url, + 'User-Agent': random_user_agent(), + 'Pragma': 'no-cache', + 'Cache-Control': 'no-cache', + 'Content-Type': 'application/json', + 'Referer': article_url, } br = browser() req = Request( endpoint_url, headers=headers, data=json.dumps(data), - method="POST", + method='POST', timeout=self.timeout, ) res = br.open(req) article = json.loads(res.read()) - new_soup = self.soup(article["content"]) + new_soup = self.soup(article['content']) # clear out existing partial content for c in list(content_ele.children): c.extract() # use extract() instead of decompose() because of strings @@ -140,52 +140,52 @@ class HBR(BasicNewsRecipe): def parse_index(self): d = self.recipe_specific_options.get('issue') if not (d and isinstance(d, str)): - soup = self.index_to_soup(f"{self.base_url}/magazine") - a = soup.find("a", href=lambda x: x and x.startswith("/archive-toc/")) - cov_url = a.find("img", attrs={"src": True})["src"] + soup = self.index_to_soup(f'{self.base_url}/magazine') + a = soup.find('a', href=lambda x: x and x.startswith('/archive-toc/')) + cov_url = a.find('img', attrs={'src': True})['src'] self.cover_url = urljoin(self.base_url, cov_url) - issue_url = urljoin(self.base_url, a["href"]) + issue_url = urljoin(self.base_url, a['href']) else: issue_url = 'https://hbr.org/archive-toc/BR' + d - mobj = re.search(r"archive-toc/(?P(BR)?\d+)\b", issue_url) + mobj = re.search(r'archive-toc/(?P(BR)?\d+)\b', issue_url) if mobj: self.cover_url = f'https://hbr.org/resources/images/covers/{mobj.group("issue")}_500.png' - self.log("Downloading issue:", issue_url) + self.log('Downloading issue:', issue_url) soup = self.index_to_soup(issue_url) - issue_title = soup.find("h1") + issue_title = soup.find('h1') if issue_title: - self.timefmt = f" [{self.tag_to_string(issue_title)}]" + self.timefmt = f' [{self.tag_to_string(issue_title)}]' feeds = OrderedDict() - for h3 in soup.find_all("h3", attrs={"class": "hed"}): - article_link_ele = h3.find("a") + for h3 in soup.find_all('h3', attrs={'class': 'hed'}): + article_link_ele = h3.find('a') if not article_link_ele: continue article_ele = h3.find_next_sibling( - "div", attrs={"class": "stream-item-info"} + 'div', attrs={'class': 'stream-item-info'} ) if not article_ele: continue title = self.tag_to_string(article_link_ele) - url = urljoin(self.base_url, article_link_ele["href"]) + url = urljoin(self.base_url, article_link_ele['href']) - authors_ele = article_ele.select("ul.byline li") - authors = ", ".join([self.tag_to_string(a) for a in authors_ele]) + authors_ele = article_ele.select('ul.byline li') + authors = ', '.join([self.tag_to_string(a) for a in authors_ele]) - article_desc = "" - dek_ele = h3.find_next_sibling("div", attrs={"class": "dek"}) + article_desc = '' + dek_ele = h3.find_next_sibling('div', attrs={'class': 'dek'}) if dek_ele: - article_desc = self.tag_to_string(dek_ele) + " | " + authors + article_desc = self.tag_to_string(dek_ele) + ' | ' + authors section_ele = ( - h3.findParent("li") - .find_previous_sibling("div", **classes("stream-section-label")) - .find("h4") + h3.findParent('li') + .find_previous_sibling('div', **classes('stream-section-label')) + .find('h4') ) section_title = self.tag_to_string(section_ele).title() feeds.setdefault(section_title, []).append( - {"title": title, "url": url, "description": article_desc} + {'title': title, 'url': url, 'description': article_desc} ) return feeds.items() diff --git a/recipes/heise.recipe b/recipes/heise.recipe index 886fad31cf..cae53ef938 100644 --- a/recipes/heise.recipe +++ b/recipes/heise.recipe @@ -68,4 +68,4 @@ class heiseDe(BasicNewsRecipe): ] def get_article_url(self, article): - return article.link + "&view=print" + return article.link + '&view=print' diff --git a/recipes/heise_ct.recipe b/recipes/heise_ct.recipe index 66ec19a02b..c48c29f9a6 100644 --- a/recipes/heise_ct.recipe +++ b/recipes/heise_ct.recipe @@ -108,7 +108,7 @@ class heise_select(BasicNewsRecipe): img = soup.new_tag('img', src=aimg['href'], alt=aimg['data-pswp-bu'], - style="display: block;") + style='display: block;') if img is not None: aimg.replaceWith(img) diff --git a/recipes/heise_ix.recipe b/recipes/heise_ix.recipe index dadbf1ac94..d4d7fcd1b3 100644 --- a/recipes/heise_ix.recipe +++ b/recipes/heise_ix.recipe @@ -109,7 +109,7 @@ class heise_select(BasicNewsRecipe): 'img', src=aimg['href'], alt=aimg['data-pswp-bu'], - style="display: block;" + style='display: block;' ) if img is not None: aimg.replaceWith(img) diff --git a/recipes/hindu.recipe b/recipes/hindu.recipe index c1719195f8..b49539803a 100644 --- a/recipes/hindu.recipe +++ b/recipes/hindu.recipe @@ -17,7 +17,7 @@ def absurl(url): class TheHindu(BasicNewsRecipe): title = 'The Hindu' __author__ = 'unkn0wn' - description = 'Articles from The Hindu, Today\'s Paper.' + description = "Articles from The Hindu, Today's Paper." language = 'en_IN' no_stylesheets = True masthead_url = 'https://www.thehindu.com/theme/images/th-online/thehindu-logo.svg' @@ -133,7 +133,7 @@ class TheHindu(BasicNewsRecipe): url = absurl(item['href']) desc = 'Page no.' + item['pageno'] + ' | ' + item['teaser_text'] or '' self.log(' ', title, '\n\t', url) - feeds_dict[section].append({"title": title, "url": url, "description": desc}) + feeds_dict[section].append({'title': title, 'url': url, 'description': desc}) return [(section, articles) for section, articles in feeds_dict.items()] else: return [] diff --git a/recipes/hindustan_times_print.recipe b/recipes/hindustan_times_print.recipe index e90d28a446..9bf5d34f39 100644 --- a/recipes/hindustan_times_print.recipe +++ b/recipes/hindustan_times_print.recipe @@ -97,7 +97,7 @@ class ht(BasicNewsRecipe): continue desc = page_no self.log('\t', title, ' ', desc) - feeds_dict[section].append({"title": title, "description": desc, "url": url}) + feeds_dict[section].append({'title': title, 'description': desc, 'url': url}) return [(section, articles) for section, articles in feeds_dict.items()] diff --git a/recipes/history_today.recipe b/recipes/history_today.recipe index eed6efad0a..b7f1462567 100644 --- a/recipes/history_today.recipe +++ b/recipes/history_today.recipe @@ -59,23 +59,23 @@ class HistoryToday(BasicNewsRecipe): feeds = OrderedDict() section_title = '' - for section in div.findAll('div', attrs={'id': re.compile(r"block\-views\-contents.*")}): + for section in div.findAll('div', attrs={'id': re.compile(r'block\-views\-contents.*')}): section_title = self.tag_to_string( section.find('h2', attrs={'class': 'title'})) sectionbody = section.find('div', attrs={'class': 'view-content'}) - for article in sectionbody.findAll('div', attrs={'class': re.compile(r"views\-row.*")}): + for article in sectionbody.findAll('div', attrs={'class': re.compile(r'views\-row.*')}): articles = [] subarticle = [] subarticle = article.findAll('div') if len(subarticle) < 2: continue title = self.tag_to_string(subarticle[0]) - originalurl = "https://www.historytoday.com" + \ + originalurl = 'https://www.historytoday.com' + \ subarticle[0].span.a['href'].strip() originalpage = self.index_to_soup(originalurl) printurl = originalpage.find( 'div', attrs={'id': 'ht-tools'}).a['href'].strip() - url = "https://www.historytoday.com" + printurl + url = 'https://www.historytoday.com' + printurl desc = self.tag_to_string(subarticle[1]) articles.append({'title': title, 'url': url, 'description': desc, 'date': ''}) diff --git a/recipes/hoy.recipe b/recipes/hoy.recipe index 8811b0e079..dd40c12488 100644 --- a/recipes/hoy.recipe +++ b/recipes/hoy.recipe @@ -69,7 +69,7 @@ class Hoy(BasicNewsRecipe): def preprocess_html(self, soup): soup.html['dir'] = self.direction mcharset = new_tag(soup, 'meta', [ - ("http-equiv", "Content-Type"), ("content", "text/html; charset=utf-8")]) + ('http-equiv', 'Content-Type'), ('content', 'text/html; charset=utf-8')]) soup.head.insert(0, mcharset) for item in soup.findAll(style=True): del item['style'] diff --git a/recipes/hurriyet.recipe b/recipes/hurriyet.recipe index 26577b4faa..697fc037dd 100644 --- a/recipes/hurriyet.recipe +++ b/recipes/hurriyet.recipe @@ -35,8 +35,8 @@ class Hurriyet(BasicNewsRecipe): compress_news_images = True # some mild formatting - extra_css = """.news-media { clear: left; } - .news-detail-title { clear:left; }""" + extra_css = '''.news-media { clear: left; } + .news-detail-title { clear:left; }''' keep_only_tags = [ # title diff --git a/recipes/idnes.recipe b/recipes/idnes.recipe index af359f2ec2..cca9499fb1 100644 --- a/recipes/idnes.recipe +++ b/recipes/idnes.recipe @@ -39,7 +39,7 @@ class iHeuteRecipe(BasicNewsRecipe): def print_version(self, url): print_url = url - split_url = url.split("?") + split_url = url.split('?') if (split_url[0].rfind('dilbert.asp') != -1): # dilbert komix print_url = print_url.replace('.htm', '.gif&tisk=1') print_url = print_url.replace('.asp', '.aspx') diff --git a/recipes/ieee_spectrum_mag.recipe b/recipes/ieee_spectrum_mag.recipe index 11c65b91f2..9575beb77e 100644 --- a/recipes/ieee_spectrum_mag.recipe +++ b/recipes/ieee_spectrum_mag.recipe @@ -7,18 +7,18 @@ from calibre.web.feeds.news import BasicNewsRecipe class IEEESpectrumMagazine(BasicNewsRecipe): - title = "IEEE Spectrum Magazine" + title = 'IEEE Spectrum Magazine' language = 'en' __author__ = 'yodha8' - description = "Published on day 1 of every month." + description = 'Published on day 1 of every month.' oldest_article = 120 # Mag gathers articles published older than a month online. So we scan for 4 months in the feed. max_articles_per_feed = 100 auto_cleanup = True # RSS feed for the current month now = datetime.datetime.now() - year_month = now.strftime("%Y/%B").lower() - month_feed_url = "https://spectrum.ieee.org/feeds/magazine/{}.rss".format( + year_month = now.strftime('%Y/%B').lower() + month_feed_url = 'https://spectrum.ieee.org/feeds/magazine/{}.rss'.format( year_month ) @@ -28,8 +28,8 @@ class IEEESpectrumMagazine(BasicNewsRecipe): def get_cover_url(self): """Go to this month's URL and pull cover image from there.""" - month_url = "https://spectrum.ieee.org/magazine/{}".format(self.year_month) + month_url = 'https://spectrum.ieee.org/magazine/{}'.format(self.year_month) soup = self.index_to_soup(month_url) - img_meta = soup.find("meta", property="og:image") - img_url = img_meta["content"] + img_meta = soup.find('meta', property='og:image') + img_url = img_meta['content'] return img_url diff --git a/recipes/il_messaggero.recipe b/recipes/il_messaggero.recipe index ad6ac66af7..0743007cdf 100644 --- a/recipes/il_messaggero.recipe +++ b/recipes/il_messaggero.recipe @@ -43,15 +43,15 @@ class IlMessaggero(BasicNewsRecipe): cover = None st = time.localtime() year = str(st.tm_year) - month = "%.2d" % st.tm_mon - day = "%.2d" % st.tm_mday + month = '%.2d' % st.tm_mon + day = '%.2d' % st.tm_mday cover = 'http://carta.ilmessaggero.it/' + year + \ month + day + '/jpeg/MSGR_20_CITTA_1.jpg' br = BasicNewsRecipe.get_browser(self) try: br.open(cover) except: - self.log("\nCover unavailable") + self.log('\nCover unavailable') cover = 'http://www.ilmessaggero.it/img_tst/logomsgr.gif' return cover diff --git a/recipes/il_post.recipe b/recipes/il_post.recipe index 42a08d5964..cd45d211b4 100644 --- a/recipes/il_post.recipe +++ b/recipes/il_post.recipe @@ -21,20 +21,20 @@ dates = [ date.today().strftime('%Y/%m/%d'), (date.today() - timedelta(1)).strft # Comment (add # in front) to disable the sections you are not interested in # Commenta (aggiungi # davanti alla riga) per disabilitare le sezioni che non vuoi scaricare sections = [ - ("Italia", "https://www.ilpost.it/italia/"), - ("Mondo", "https://www.ilpost.it/mondo/"), - ("Politica", "https://www.ilpost.it/politica/"), - ("Tecnologia", "https://www.ilpost.it/tecnologia/"), - ("Internet", "https://www.ilpost.it/internet/"), - ("Scienza", "https://www.ilpost.it/scienza/"), - ("Cultura", "https://www.ilpost.it/cultura/"), - ("Economia", "https://www.ilpost.it/economia/"), - ("Sport", "https://www.ilpost.it/sport/"), - ("Media", "https://www.ilpost.it/media/"), - ("Moda", "https://www.ilpost.it/moda/"), - ("Libri", "https://www.ilpost.it/libri/"), - ("Auto", "https://www.ilpost.it/auto/"), - ("Konrad", "https://www.ilpost.it/europa/"), + ('Italia', 'https://www.ilpost.it/italia/'), + ('Mondo', 'https://www.ilpost.it/mondo/'), + ('Politica', 'https://www.ilpost.it/politica/'), + ('Tecnologia', 'https://www.ilpost.it/tecnologia/'), + ('Internet', 'https://www.ilpost.it/internet/'), + ('Scienza', 'https://www.ilpost.it/scienza/'), + ('Cultura', 'https://www.ilpost.it/cultura/'), + ('Economia', 'https://www.ilpost.it/economia/'), + ('Sport', 'https://www.ilpost.it/sport/'), + ('Media', 'https://www.ilpost.it/media/'), + ('Moda', 'https://www.ilpost.it/moda/'), + ('Libri', 'https://www.ilpost.it/libri/'), + ('Auto', 'https://www.ilpost.it/auto/'), + ('Konrad', 'https://www.ilpost.it/europa/'), ] # ----------- CUSTOMIZATION OPTIONS OVER ----------- @@ -45,16 +45,16 @@ class IlPost(BasicNewsRecipe): __license__ = 'GPL v3' __copyright__ = '2019, Marco Scirea ' - title = "Il Post" - language = "it" + title = 'Il Post' + language = 'it' description = ( 'Puoi decidere quali sezioni scaricare modificando la ricetta.' ' Di default le immagini sono convertite in scala di grigio per risparmiare spazio,' - ' la ricetta puo\' essere configurata per tenerle a colori' + " la ricetta puo' essere configurata per tenerle a colori" ) - tags = "news" + tags = 'news' masthead_url = 'https://www.ilpost.it/error/images/ilpost.svg' - ignore_duplicate_articles = {"title", "url"} + ignore_duplicate_articles = {'title', 'url'} no_stylesheets = True extra_css = ' .wp-caption-text { font-size:small; } ' keep_only_tags = [dict(name='main', attrs={'id':lambda x: x and x.startswith('index_main-content__')})] @@ -81,9 +81,9 @@ class IlPost(BasicNewsRecipe): continue self.log('\t', title) entries.append({ - "url": link["href"], - "title": title, - "description": desc + 'url': link['href'], + 'title': title, + 'description': desc }) return (name, entries) diff --git a/recipes/ilsole24ore.recipe b/recipes/ilsole24ore.recipe index e8b678155b..2bfaaac747 100644 --- a/recipes/ilsole24ore.recipe +++ b/recipes/ilsole24ore.recipe @@ -44,13 +44,13 @@ class IlSole24Ore(BasicNewsRecipe): link = article.get('link', None) if link is None: return article - if link.split('/')[-1] == "story01.htm": + if link.split('/')[-1] == 'story01.htm': link = link.split('/')[-2] a = ['0B', '0C', '0D', '0E', '0F', '0G', '0N', '0L0S', '0A'] b = ['.', '/', '?', '-', '=', '&', '.com', 'www.', '0'] for i in range(0, len(a)): link = link.replace(a[i], b[i]) - link = "http://" + link + link = 'http://' + link return link feeds = [ diff --git a/recipes/inc.recipe b/recipes/inc.recipe index f1dfa19c2a..501bff65e8 100644 --- a/recipes/inc.recipe +++ b/recipes/inc.recipe @@ -40,7 +40,7 @@ class IncMagazineRecipe(BasicNewsRecipe): def get_browser(self): def has_login_name(form): try: - form.find_control(name="email") + form.find_control(name='email') except: return False else: diff --git a/recipes/independent_australia.recipe b/recipes/independent_australia.recipe index 1e2f5148da..d1fadf3a4a 100644 --- a/recipes/independent_australia.recipe +++ b/recipes/independent_australia.recipe @@ -46,7 +46,7 @@ class IndependentAustralia(BasicNewsRecipe): remove_javascript = True keep_only_tags = [ - dict(name='div', attrs={'class': "art-display"}) + dict(name='div', attrs={'class': 'art-display'}) ] # the article content is contained in # ************************************ diff --git a/recipes/india_today.recipe b/recipes/india_today.recipe index 3f2c960a3b..548edb323e 100644 --- a/recipes/india_today.recipe +++ b/recipes/india_today.recipe @@ -75,7 +75,7 @@ class IndiaToday(BasicNewsRecipe): section = x[0] try: return ( - 'Editor\'s Note', 'Cover Story', 'The Big Story', 'Upfront', + "Editor's Note", 'Cover Story', 'The Big Story', 'Upfront', 'NATION', 'INTERVIEW' ).index(section) except Exception: diff --git a/recipes/indian_express.recipe b/recipes/indian_express.recipe index cd94f157cc..6a185992d4 100644 --- a/recipes/indian_express.recipe +++ b/recipes/indian_express.recipe @@ -136,13 +136,13 @@ class IndianExpress(BasicNewsRecipe): return citem['content'].replace('300', '600') def preprocess_html(self, soup): - if h2 := (soup.find(attrs={"itemprop": "description"}) or soup.find(**classes("synopsis"))): + if h2 := (soup.find(attrs={'itemprop': 'description'}) or soup.find(**classes('synopsis'))): h2.name = 'p' h2['id'] = 'sub-d' for span in soup.findAll( - "span", attrs={"class": ["ie-custom-caption", "custom-caption"]} + 'span', attrs={'class': ['ie-custom-caption', 'custom-caption']} ): - span["id"] = "img-cap" + span['id'] = 'img-cap' for img in soup.findAll('img', attrs={'data-src': True}): img['src'] = img['data-src'] if span := soup.find('span', content=True, attrs={'itemprop': 'dateModified'}): diff --git a/recipes/ing_dk.recipe b/recipes/ing_dk.recipe index e75e24d5a8..ff47f9da78 100644 --- a/recipes/ing_dk.recipe +++ b/recipes/ing_dk.recipe @@ -21,9 +21,9 @@ class Ing_dk(BasicNewsRecipe): auto_cleanup = True keep_only_tags = [ - dict(name="div", attrs={'class': 'menu-article-current-title'}), - dict(name="section", attrs={'class': 'byline'}), - dict(name="section", attrs={'class': 'body'}), + dict(name='div', attrs={'class': 'menu-article-current-title'}), + dict(name='section', attrs={'class': 'byline'}), + dict(name='section', attrs={'class': 'body'}), ] feeds = [ diff --git a/recipes/instapaper.recipe b/recipes/instapaper.recipe index 7422b2bb4a..c7ecf69381 100644 --- a/recipes/instapaper.recipe +++ b/recipes/instapaper.recipe @@ -10,7 +10,7 @@ from calibre.web.feeds.news import BasicNewsRecipe # The Gutenweb stylesheet from https://www.mobileread.com/forums/showpost.php?p=2809828&postcount=31 -gutenweb = """"html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:\'\';content:none}table{border-collapse:collapse;border-spacing:0}html,:root{font-size:16px}body{font-size:1em;line-height:1.5em;margin-top:1.5em;margin-bottom:1.5em;max-width:33em;margin-left:auto;margin-right:auto;font-family:Helvetica,Arial,sans-serif;text-align:left;word-spacing:normal;hyphens:auto;orphans:2;widows:2;font-variant-numeric:oldstyle-nums}body *{max-width:100%}address,article,aside,audio,canvas,footer,header,ol,ul,dl,pre,section,table,video,img,figure{margin-top:1.5em;margin-bottom:1.5em}p{margin-top:1.5em;margin-bottom:0em}p+p{margin-top:0em;margin-bottom:0em;text-indent:1.5em}h1{font-size:2.25em;line-height:1.33333em;margin-top:0.66667em;margin-bottom:0.66667em}h2{font-size:1.5em;line-height:1em;margin-top:1em;margin-bottom:1em}h3{font-size:1.3125em;line-height:1.14286em;margin-top:1.14286em;margin-bottom:1.14286em}h4{font-size:1.125em;line-height:1.33333em;margin-top:1.33333em;margin-bottom:1.33333em}h1,h2,h3,h4,h5,h6{font-family:Georgia,serif;font-weight:bold;page-break-after:avoid}ul li{list-style-type:disc}ol li{list-style-type:decimal}li{list-style-position:inside;text-indent:1.5em}dt{font-weight:bold;float:left;margin-right:1.5em}tr{page-break-before:avoid;page-break-after:avoid}td,th{outline:0.1em solid #000;padding:0 0.5em;text-align:left}tfoot td{font-style:italic}caption{font-style:italic;text-align:center;font-style:italic}blockquote{margin-top:2.25em;margin-bottom:2.25em;margin-left:2.25em;margin-right:2.25em}blockquote p{margin-top:0em;margin-bottom:0em;text-indent:0}figure{text-align:center}figure img,figure audio,figure canvas,figure video,figure table{margin-top:0;margin-bottom:0}figcaption{font-size:0.875em;line-height:1.71429em;margin-top:0em;margin-bottom:1.71429em;font-style:italic}img{vertical-align:bottom}code,samp,kbd,var{font-family:Consolas,"Liberation Mono",Courier,monospace;font-size:0.875em;font-weight:normal;font-style:normal;text-decoration:none;line-height:0.875em;padding:0 0.3em}mark{background:#ff0;color:#000}code,.code,samp,kbd,var{background-color:#f8f8f8;box-shadow:0 0 0.1em 0.1em #ddd}em{font-style:italic}strong{font-weight:bold}abbr{letter-spacing:0.1em}abbr[title]{border-bottom:1px dotted #000}cite,q{font-style:italic}q{font-style:italic;quotes:"\xe2\x80\x9c" "\xe2\x80\x9d" "\xe2\x80\x98" "\xe2\x80\x99"}q:before{content:open-quote}q:after{content:close-quote}dfn{font-style:italic}sup,sub{font-size:70%;line-height:70%;position:relative}sup{top:-0.5em}sub{top:0.5em}hr{border-bottom:0.0625em solid #000;border-top:0 none;border-left:0 none;border-right:0 none;margin-top:1.4375em;margin-bottom:1.5em}small{font-size:0.875em;line-height:1.71429em;margin-top:1.71429em;margin-bottom:1.71429em}i{font-style:italic}b{font-weight:bold}u{text-decoration:underline}s{text-decoration:line-through}ins{font-weight:bold;text-decoration:underline}del{text-decoration:line-through}.caps,.nums{letter-spacing:0.1em}.caps{font-variant-numeric:lining-nums}.code{overflow:auto;padding:0 1em;background-color:#f8f8f8;box-shadow:0 0 0.1em 0.1em #ddd}.code code,.code samp,.code kbd,.code var{box-shadow:none;padding:0}.chapter{page-break-after:auto;page-break-before:always}.note{text-indent:0;font-size:0.875em;line-height:1.71429em;margin-top:1.71429em;margin-bottom:1.71429em}.verse{font-family:inherit;display:table;width:auto;margin-left:auto;margin-right:auto}.toc{margin:0 auto}.toc td,.toc th{outline:0 none}.toc th{padding:0 0.5em 0 0;text-align:right;font-weight:normal}.toc td:before{content:"\\2022";padding-right:0.5em}.toc td{padding:0;text-align:left;font-style:italic}@page{margin-top:72pt;margin-bottom:72pt}@media print{body{font-size:12pt;line-height:18pt;margin-top:0pt;margin-bottom:0pt;font-family:"Times New Roman",Times,serif}p{margin-top:18pt;margin-bottom:0pt}p+p{text-indent:18pt}address,article,aside,audio,canvas,footer,header,ol,ul,dl,pre,section,table,video,img,figure{margin-top:18pt;margin-bottom:18pt}h1{font-size:21pt;line-height:36pt;margin-top:18pt;margin-bottom:18pt}h2{font-size:18pt;line-height:18pt;margin-top:18pt;margin-bottom:18pt}h3{font-size:16pt;line-height:18pt;margin-top:18pt;margin-bottom:18pt}h4{font-size:14pt;line-height:18pt;margin-top:18pt;margin-bottom:18pt}dt{margin-right:18pt}li{text-indent:18pt}blockquote{margin-top:27pt;margin-bottom:27pt;margin-left:27pt;margin-right:27pt}blockquote p{margin-top:0em;margin-bottom:0em;text-indent:0}figcaption{font-size:10pt;line-height:18pt;margin-top:0pt;margin-bottom:18pt}pre{white-space:pre-line}abbr[title]{border-bottom:0 none}small{font-size:10pt;line-height:18pt;margin-top:18pt;margin-bottom:18pt}hr{border-bottom:0.08333em solid #000;margin-top:17pt;margin-bottom:18pt}.note{font-size:10pt;line-height:18pt;margin-top:18pt;margin-bottom:18pt}}""" # noqa: E501 +gutenweb = '''"html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:\'\';content:none}table{border-collapse:collapse;border-spacing:0}html,:root{font-size:16px}body{font-size:1em;line-height:1.5em;margin-top:1.5em;margin-bottom:1.5em;max-width:33em;margin-left:auto;margin-right:auto;font-family:Helvetica,Arial,sans-serif;text-align:left;word-spacing:normal;hyphens:auto;orphans:2;widows:2;font-variant-numeric:oldstyle-nums}body *{max-width:100%}address,article,aside,audio,canvas,footer,header,ol,ul,dl,pre,section,table,video,img,figure{margin-top:1.5em;margin-bottom:1.5em}p{margin-top:1.5em;margin-bottom:0em}p+p{margin-top:0em;margin-bottom:0em;text-indent:1.5em}h1{font-size:2.25em;line-height:1.33333em;margin-top:0.66667em;margin-bottom:0.66667em}h2{font-size:1.5em;line-height:1em;margin-top:1em;margin-bottom:1em}h3{font-size:1.3125em;line-height:1.14286em;margin-top:1.14286em;margin-bottom:1.14286em}h4{font-size:1.125em;line-height:1.33333em;margin-top:1.33333em;margin-bottom:1.33333em}h1,h2,h3,h4,h5,h6{font-family:Georgia,serif;font-weight:bold;page-break-after:avoid}ul li{list-style-type:disc}ol li{list-style-type:decimal}li{list-style-position:inside;text-indent:1.5em}dt{font-weight:bold;float:left;margin-right:1.5em}tr{page-break-before:avoid;page-break-after:avoid}td,th{outline:0.1em solid #000;padding:0 0.5em;text-align:left}tfoot td{font-style:italic}caption{font-style:italic;text-align:center;font-style:italic}blockquote{margin-top:2.25em;margin-bottom:2.25em;margin-left:2.25em;margin-right:2.25em}blockquote p{margin-top:0em;margin-bottom:0em;text-indent:0}figure{text-align:center}figure img,figure audio,figure canvas,figure video,figure table{margin-top:0;margin-bottom:0}figcaption{font-size:0.875em;line-height:1.71429em;margin-top:0em;margin-bottom:1.71429em;font-style:italic}img{vertical-align:bottom}code,samp,kbd,var{font-family:Consolas,"Liberation Mono",Courier,monospace;font-size:0.875em;font-weight:normal;font-style:normal;text-decoration:none;line-height:0.875em;padding:0 0.3em}mark{background:#ff0;color:#000}code,.code,samp,kbd,var{background-color:#f8f8f8;box-shadow:0 0 0.1em 0.1em #ddd}em{font-style:italic}strong{font-weight:bold}abbr{letter-spacing:0.1em}abbr[title]{border-bottom:1px dotted #000}cite,q{font-style:italic}q{font-style:italic;quotes:"\xe2\x80\x9c" "\xe2\x80\x9d" "\xe2\x80\x98" "\xe2\x80\x99"}q:before{content:open-quote}q:after{content:close-quote}dfn{font-style:italic}sup,sub{font-size:70%;line-height:70%;position:relative}sup{top:-0.5em}sub{top:0.5em}hr{border-bottom:0.0625em solid #000;border-top:0 none;border-left:0 none;border-right:0 none;margin-top:1.4375em;margin-bottom:1.5em}small{font-size:0.875em;line-height:1.71429em;margin-top:1.71429em;margin-bottom:1.71429em}i{font-style:italic}b{font-weight:bold}u{text-decoration:underline}s{text-decoration:line-through}ins{font-weight:bold;text-decoration:underline}del{text-decoration:line-through}.caps,.nums{letter-spacing:0.1em}.caps{font-variant-numeric:lining-nums}.code{overflow:auto;padding:0 1em;background-color:#f8f8f8;box-shadow:0 0 0.1em 0.1em #ddd}.code code,.code samp,.code kbd,.code var{box-shadow:none;padding:0}.chapter{page-break-after:auto;page-break-before:always}.note{text-indent:0;font-size:0.875em;line-height:1.71429em;margin-top:1.71429em;margin-bottom:1.71429em}.verse{font-family:inherit;display:table;width:auto;margin-left:auto;margin-right:auto}.toc{margin:0 auto}.toc td,.toc th{outline:0 none}.toc th{padding:0 0.5em 0 0;text-align:right;font-weight:normal}.toc td:before{content:"\\2022";padding-right:0.5em}.toc td{padding:0;text-align:left;font-style:italic}@page{margin-top:72pt;margin-bottom:72pt}@media print{body{font-size:12pt;line-height:18pt;margin-top:0pt;margin-bottom:0pt;font-family:"Times New Roman",Times,serif}p{margin-top:18pt;margin-bottom:0pt}p+p{text-indent:18pt}address,article,aside,audio,canvas,footer,header,ol,ul,dl,pre,section,table,video,img,figure{margin-top:18pt;margin-bottom:18pt}h1{font-size:21pt;line-height:36pt;margin-top:18pt;margin-bottom:18pt}h2{font-size:18pt;line-height:18pt;margin-top:18pt;margin-bottom:18pt}h3{font-size:16pt;line-height:18pt;margin-top:18pt;margin-bottom:18pt}h4{font-size:14pt;line-height:18pt;margin-top:18pt;margin-bottom:18pt}dt{margin-right:18pt}li{text-indent:18pt}blockquote{margin-top:27pt;margin-bottom:27pt;margin-left:27pt;margin-right:27pt}blockquote p{margin-top:0em;margin-bottom:0em;text-indent:0}figcaption{font-size:10pt;line-height:18pt;margin-top:0pt;margin-bottom:18pt}pre{white-space:pre-line}abbr[title]{border-bottom:0 none}small{font-size:10pt;line-height:18pt;margin-top:18pt;margin-bottom:18pt}hr{border-bottom:0.08333em solid #000;margin-top:17pt;margin-bottom:18pt}.note{font-size:10pt;line-height:18pt;margin-top:18pt;margin-bottom:18pt}}''' # noqa: E501 class InstapaperRecipe(BasicNewsRecipe): diff --git a/recipes/internazionale.recipe b/recipes/internazionale.recipe index d2c1a0bbfc..19540caf93 100644 --- a/recipes/internazionale.recipe +++ b/recipes/internazionale.recipe @@ -29,13 +29,13 @@ class Volkskrant(BasicNewsRecipe): ), dict(name=['script', 'style']), ] - remove_attributes = ["class", "id", "name", "style"] + remove_attributes = ['class', 'id', 'name', 'style'] encoding = 'utf-8' no_stylesheets = True ignore_duplicate_articles = {'url'} - current_number_url = "https://www.internazionale.it/sommario" - home_url = "https://www.internazionale.it" + current_number_url = 'https://www.internazionale.it/sommario' + home_url = 'https://www.internazionale.it' cover_url = None def extract_article(self, article): diff --git a/recipes/iol_za.recipe b/recipes/iol_za.recipe index 48072617eb..8ddc433fb9 100644 --- a/recipes/iol_za.recipe +++ b/recipes/iol_za.recipe @@ -23,10 +23,10 @@ class IOL_za(BasicNewsRecipe): remove_empty_feeds = True publication_type = 'newsportal' masthead_url = 'http://www.iol.co.za/polopoly_fs/iol-news5-1.989381!/image/464471284.png_gen/derivatives/absolute/464471284.png' - extra_css = """ + extra_css = ''' body{font-family: Arial,Helvetica,sans-serif } img{display: block} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/iprofesional.recipe b/recipes/iprofesional.recipe index 27dad0a157..c2bbb98246 100644 --- a/recipes/iprofesional.recipe +++ b/recipes/iprofesional.recipe @@ -25,7 +25,7 @@ class iProfesional(BasicNewsRecipe): remove_empty_feeds = True publication_type = 'newsportal' masthead_url = 'http://www.iprofesional.com/img/header/logoiprofesional.png' - extra_css = """ + extra_css = ''' body{font-family: "Open Sans", sans-serif} img{margin-bottom: 0.4em; display:block} .tituloprincipal{font-family: WhitneyBold, Arial, sans-serif; @@ -33,7 +33,7 @@ class iProfesional(BasicNewsRecipe): font-size: x-large; display: block; margin-bottom: 1em;} .bajadanh{font-size: small; display: block; margin-bottom: 1em;} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/jacobinmag.recipe b/recipes/jacobinmag.recipe index 5804d55fec..2338d62c7c 100644 --- a/recipes/jacobinmag.recipe +++ b/recipes/jacobinmag.recipe @@ -35,11 +35,11 @@ class Jacobinmag(BasicNewsRecipe): issue_url = None PREFIX = 'https://www.jacobinmag.com' LOGIN = 'https://auth.jacobinmag.com/mini_profile?redirect=https%3A%2F%2Fwww.jacobinmag.com%2F' - extra_css = """ + extra_css = ''' body{font-family: Antwerp, 'Times New Roman', Times, serif} img{margin-top:1em; margin-bottom: 1em; display:block} .entry-dek,.entry-author{font-family: Hurme-No3, Futura, sans-serif} - """ + ''' conversion_options = { 'comment': description, diff --git a/recipes/japan_times.recipe b/recipes/japan_times.recipe index eb1db6493c..fad4f6eb2b 100644 --- a/recipes/japan_times.recipe +++ b/recipes/japan_times.recipe @@ -1,36 +1,36 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -__license__ = "GPL v3" +__license__ = 'GPL v3' __copyright__ = ( - "2008-2013, Darko Miletic . " - "2022, Albert Aparicio Isarn " + '2008-2013, Darko Miletic . ' + '2022, Albert Aparicio Isarn ' ) -""" +''' japantimes.co.jp -""" +''' from calibre.web.feeds.news import BasicNewsRecipe class JapanTimes(BasicNewsRecipe): - title = "The Japan Times" - __author__ = "Albert Aparicio Isarn (original recipe by Darko Miletic)" + title = 'The Japan Times' + __author__ = 'Albert Aparicio Isarn (original recipe by Darko Miletic)' description = ( "The latest news from Japan Times, Japan's leading English-language daily newspaper" ) - language = "en_JP" - category = "news, politics, japan" - publisher = "The Japan Times" + language = 'en_JP' + category = 'news, politics, japan' + publisher = 'The Japan Times' oldest_article = 2 max_articles_per_feed = 150 no_stylesheets = True remove_javascript = True use_embedded_content = False - encoding = "utf8" - publication_type = "newspaper" - masthead_url = "https://cdn-japantimes.com/wp-content/themes/jt_theme/library/img/japantimes-logo-tagline.png" - extra_css = "body{font-family: Geneva,Arial,Helvetica,sans-serif}" + encoding = 'utf8' + publication_type = 'newspaper' + masthead_url = 'https://cdn-japantimes.com/wp-content/themes/jt_theme/library/img/japantimes-logo-tagline.png' + extra_css = 'body{font-family: Geneva,Arial,Helvetica,sans-serif}' recipe_specific_options = { 'days': { @@ -47,37 +47,37 @@ class JapanTimes(BasicNewsRecipe): self.oldest_article = float(d) conversion_options = { - "comment": description, - "tags": category, - "publisher": publisher, - "language": language, + 'comment': description, + 'tags': category, + 'publisher': publisher, + 'language': language, } - remove_tags_before = {"name": "h1"} - remove_tags_after = {"name": "ul", "attrs": {"class": "single-sns-area"}} + remove_tags_before = {'name': 'h1'} + remove_tags_after = {'name': 'ul', 'attrs': {'class': 'single-sns-area'}} keep_only_tags = [ - {"name": "div", "attrs": {"class": "padding_block"}}, + {'name': 'div', 'attrs': {'class': 'padding_block'}}, # {"name": "h5", "attrs": {"class": "writer", "role": "author"}}, # {"name": "p", "attrs": {"class": "credit"}}, ] remove_tags = [ - {"name": "div", "id": "no_js_blocker", "attrs": {"class": "padding_block"}}, - {"name": "div", "attrs": {"class": "single-upper-meta"}}, - {"name": "ul", "attrs": {"class": "single-sns-area"}}, + {'name': 'div', 'id': 'no_js_blocker', 'attrs': {'class': 'padding_block'}}, + {'name': 'div', 'attrs': {'class': 'single-upper-meta'}}, + {'name': 'ul', 'attrs': {'class': 'single-sns-area'}}, ] feeds = [ - (u"Top Stories", u"https://www.japantimes.co.jp/feed/topstories/"), - (u"News", u"https://www.japantimes.co.jp/news/feed/"), - (u"Opinion", u"https://www.japantimes.co.jp/opinion/feed/"), - (u"Life", u"https://www.japantimes.co.jp/life/feed/"), - (u"Community", u"https://www.japantimes.co.jp/community/feed/"), - (u"Culture", u"https://www.japantimes.co.jp/culture/feed/"), - (u"Sports", u"https://www.japantimes.co.jp/sports/feed/"), + (u'Top Stories', u'https://www.japantimes.co.jp/feed/topstories/'), + (u'News', u'https://www.japantimes.co.jp/news/feed/'), + (u'Opinion', u'https://www.japantimes.co.jp/opinion/feed/'), + (u'Life', u'https://www.japantimes.co.jp/life/feed/'), + (u'Community', u'https://www.japantimes.co.jp/community/feed/'), + (u'Culture', u'https://www.japantimes.co.jp/culture/feed/'), + (u'Sports', u'https://www.japantimes.co.jp/sports/feed/'), ] def get_article_url(self, article): rurl = BasicNewsRecipe.get_article_url(self, article) - return rurl.partition("?")[0] + return rurl.partition('?')[0] def preprocess_raw_html(self, raw, url): - return "" + raw[raw.find("") :] + return '' + raw[raw.find('') :] diff --git a/recipes/javalobby.recipe b/recipes/javalobby.recipe index dfd5396dd9..9d884cd5a6 100644 --- a/recipes/javalobby.recipe +++ b/recipes/javalobby.recipe @@ -19,10 +19,10 @@ class Engadget(BasicNewsRecipe): no_stylesheets = True use_embedded_content = False - remove_tags = [dict(name='div', attrs={'class': ["fivestar-static-form-item", "relatedContent", "pagination clearfix", "addResources"]}), - dict(name='div', attrs={'id': ["comments"]})] + remove_tags = [dict(name='div', attrs={'class': ['fivestar-static-form-item', 'relatedContent', 'pagination clearfix', 'addResources']}), + dict(name='div', attrs={'id': ['comments']})] - keep_only_tags = [dict(name='div', attrs={'id': ["article"]})] + keep_only_tags = [dict(name='div', attrs={'id': ['article']})] feeds = [(u'news', u'http://feeds.dzone.com/javalobby/frontpage')] diff --git a/recipes/jijinews.recipe b/recipes/jijinews.recipe index bd2a471e42..02728f2f6d 100644 --- a/recipes/jijinews.recipe +++ b/recipes/jijinews.recipe @@ -23,8 +23,8 @@ class JijiDotCom(BasicNewsRecipe): feeds = [(u'\u30cb\u30e5\u30fc\u30b9', u'http://www.jiji.com/rss/ranking.rdf')] - remove_tags_before = dict(id="article-area") - remove_tags_after = dict(id="ad_google") + remove_tags_before = dict(id='article-area') + remove_tags_after = dict(id='ad_google') def get_cover_url(self): cover_url = 'http://www.jiji.com/img/top_header_logo2.gif' diff --git a/recipes/kirkusreviews.recipe b/recipes/kirkusreviews.recipe index 99de429ee3..c3437f2fd7 100644 --- a/recipes/kirkusreviews.recipe +++ b/recipes/kirkusreviews.recipe @@ -4,20 +4,20 @@ from calibre.web.feeds.news import BasicNewsRecipe class KirkusReviews(BasicNewsRecipe): - title = "Kirkus Reviews" - description = ("Kirkus Reviews is an American book review magazine founded in 1933 by Virginia Kirkus." - " The magazine is headquartered in New York City. Released twice monthly on the 1st/15th.") - language = "en" - __author__ = "ping" - publication_type = "magazine" + title = 'Kirkus Reviews' + description = ('Kirkus Reviews is an American book review magazine founded in 1933 by Virginia Kirkus.' + ' The magazine is headquartered in New York City. Released twice monthly on the 1st/15th.') + language = 'en' + __author__ = 'ping' + publication_type = 'magazine' masthead_url = ( - "https://d1fd687oe6a92y.cloudfront.net/img/kir_images/logo/kirkus-nav-logo.svg" + 'https://d1fd687oe6a92y.cloudfront.net/img/kir_images/logo/kirkus-nav-logo.svg' ) - encoding = "utf-8" + encoding = 'utf-8' remove_javascript = True no_stylesheets = True auto_cleanup = False - ignore_duplicate_articles = {"url"} + ignore_duplicate_articles = {'url'} compress_news_images = True compress_news_images_auto_size = 6 max_articles_per_feed = 99 @@ -25,105 +25,105 @@ class KirkusReviews(BasicNewsRecipe): keep_only_tags = [ dict( class_=[ - "article-author", - "article-author-img-start", - "article-author-description-start", - "single-review", + 'article-author', + 'article-author-img-start', + 'article-author-description-start', + 'single-review', ] ) ] remove_tags = [ dict( class_=[ - "sidebar-content", - "article-social-share-desktop-first", - "article-social-share-desktop-pagination", - "article-social-share-mobile", - "share-review-text", - "like-dislike-article", - "rate-this-book-text", - "input-group", - "user-comments", - "show-all-response-text", - "button-row", - "hide-on-mobile", - "related-article", - "breadcrumb-row", - "shop-now-dropdown", + 'sidebar-content', + 'article-social-share-desktop-first', + 'article-social-share-desktop-pagination', + 'article-social-share-mobile', + 'share-review-text', + 'like-dislike-article', + 'rate-this-book-text', + 'input-group', + 'user-comments', + 'show-all-response-text', + 'button-row', + 'hide-on-mobile', + 'related-article', + 'breadcrumb-row', + 'shop-now-dropdown', ] ) ] - remove_tags_after = [dict(class_="single-review")] + remove_tags_after = [dict(class_='single-review')] - extra_css = """ + extra_css = ''' .image-container img { max-width: 100%; height: auto; margin-bottom: 0.2rem; } .photo-caption { font-size: 0.8rem; margin-bottom: 0.5rem; display: block; } .book-review-img .image-container { text-align: center; } .book-rating-module .description-title { font-size: 1.25rem; margin-left: 0; text-align: center; } - """ + ''' def preprocess_html(self, soup): - h1 = soup.find(class_="article-title") - book_cover = soup.find("ul", class_="book-review-img") + h1 = soup.find(class_='article-title') + book_cover = soup.find('ul', class_='book-review-img') if book_cover: - for li in book_cover.find_all("li"): - li.name = "div" - book_cover.name = "div" + for li in book_cover.find_all('li'): + li.name = 'div' + book_cover.name = 'div' if h1: book_cover.insert_before(h1.extract()) return soup def parse_index(self): - issue_url = "https://www.kirkusreviews.com/magazine/current/" + issue_url = 'https://www.kirkusreviews.com/magazine/current/' soup = self.index_to_soup(issue_url) - issue = soup.find(name="article", class_="issue-container") - cover_img = issue.select(".issue-header .cover-image img") + issue = soup.find(name='article', class_='issue-container') + cover_img = issue.select('.issue-header .cover-image img') if cover_img: - self.cover_url = cover_img[0]["src"] + self.cover_url = cover_img[0]['src'] - h1 = issue.find("h1") + h1 = issue.find('h1') if h1: - self.timefmt = f" [{self.tag_to_string(h1)}]" # edition + self.timefmt = f' [{self.tag_to_string(h1)}]' # edition articles = {} - for book_ele in soup.find_all(name="div", class_="issue-featured-book"): - link = book_ele.find("a") + for book_ele in soup.find_all(name='div', class_='issue-featured-book'): + link = book_ele.find('a') if not link: continue - section = self.tag_to_string(book_ele.find("h3")).upper() + section = self.tag_to_string(book_ele.find('h3')).upper() articles.setdefault(section, []).append( - {"url": urljoin(issue_url, link["href"]), "title": link["title"]} + {'url': urljoin(issue_url, link['href']), 'title': link['title']} ) - for post_ele in issue.select("div.issue-more-posts ul li div.lead-text"): - link = post_ele.find("a") + for post_ele in issue.select('div.issue-more-posts ul li div.lead-text'): + link = post_ele.find('a') if not link: continue - section = self.tag_to_string(post_ele.find(class_="lead-text-type")).upper() + section = self.tag_to_string(post_ele.find(class_='lead-text-type')).upper() articles.setdefault(section, []).append( { - "url": urljoin(issue_url, link["href"]), - "title": self.tag_to_string(link), + 'url': urljoin(issue_url, link['href']), + 'title': self.tag_to_string(link), } ) - for section_ele in issue.select("section.reviews-section"): + for section_ele in issue.select('section.reviews-section'): section_articles = [] - for review in section_ele.select("ul li.starred"): - link = review.select("h4 a") + for review in section_ele.select('ul li.starred'): + link = review.select('h4 a') if not link: continue - description = review.find("p") + description = review.find('p') section_articles.append( { - "url": urljoin(issue_url, link[0]["href"]), - "title": self.tag_to_string(link[0]), - "description": "" + 'url': urljoin(issue_url, link[0]['href']), + 'title': self.tag_to_string(link[0]), + 'description': '' if not description else self.tag_to_string(description), } ) if not section_articles: continue - section = self.tag_to_string(section_ele.find("h3")).upper() + section = self.tag_to_string(section_ele.find('h3')).upper() if section not in articles: articles[section] = [] articles.setdefault(section, []).extend(section_articles) diff --git a/recipes/kopalniawiedzy.recipe b/recipes/kopalniawiedzy.recipe index c9189ca643..f0e8ce9354 100644 --- a/recipes/kopalniawiedzy.recipe +++ b/recipes/kopalniawiedzy.recipe @@ -25,7 +25,7 @@ class KopalniaWiedzy(BasicNewsRecipe): {'name': 'div', 'attrs': {'class': 'article-time-and-cat'}}, {'name': 'p', 'attrs': {'class': 'tags'}}] remove_tags_after = dict(attrs={'class': 'ad-square'}) keep_only_tags = [ - dict(name="div", attrs={'class': 'article-text text-small'})] + dict(name='div', attrs={'class': 'article-text text-small'})] extra_css = '.topimage {margin-top: 30px}' preprocess_regexps = [ diff --git a/recipes/korben.recipe b/recipes/korben.recipe index 30a8b12cca..aa1b0f3492 100644 --- a/recipes/korben.recipe +++ b/recipes/korben.recipe @@ -17,6 +17,6 @@ class BasicUserRecipe1318619728(BasicNewsRecipe): try: br.open(masthead) except: - self.log("\nCover unavailable") + self.log('\nCover unavailable') masthead = None return masthead diff --git a/recipes/kudy_z_nudy.recipe b/recipes/kudy_z_nudy.recipe index 5a5b320e3a..963d3185a9 100644 --- a/recipes/kudy_z_nudy.recipe +++ b/recipes/kudy_z_nudy.recipe @@ -22,8 +22,8 @@ class kudyznudyRecipe(BasicNewsRecipe): cover_url = 'http://www.kudyznudy.cz/App_Themes/KzN/Images/Containers/Header/HeaderLogoKZN.png' remove_javascript = True no_stylesheets = True - extra_css = """ - """ + extra_css = ''' + ''' remove_attributes = [] remove_tags_before = dict( diff --git a/recipes/la_jornada.recipe b/recipes/la_jornada.recipe index cbf806c3ef..ea13c1cf7d 100644 --- a/recipes/la_jornada.recipe +++ b/recipes/la_jornada.recipe @@ -33,10 +33,10 @@ class LaJornada_mx(BasicNewsRecipe): use_embedded_content = False language = 'es_MX' remove_empty_feeds = True - cover_url = strftime("http://www.jornada.com.mx/%Y/%m/%d/portada.pdf") + cover_url = strftime('http://www.jornada.com.mx/%Y/%m/%d/portada.pdf') masthead_url = 'http://www.jornada.com.mx/v7.0/imagenes/la-jornada-trans.png' publication_type = 'newspaper' - extra_css = """ + extra_css = ''' body{font-family: "Times New Roman",serif } .cabeza{font-size: xx-large; font-weight: bold } .documentFirstHeading{font-size: xx-large; font-weight: bold } @@ -54,7 +54,7 @@ class LaJornada_mx(BasicNewsRecipe): .text{margin-top: 1.4em} p.inicial{display: inline; font-size: xx-large; font-weight: bold} p.s-s{display: inline; text-indent: 0} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/la_republica.recipe b/recipes/la_republica.recipe index 4af4575bf9..9efeaeacbd 100644 --- a/recipes/la_republica.recipe +++ b/recipes/la_republica.recipe @@ -17,7 +17,7 @@ class LaRepubblica(BasicNewsRecipe): __author__ = 'Lorenzo Vigentini, Gabriele Marini, Darko Miletic, faber1971' description = 'il quotidiano online con tutte le notizie in tempo reale. News e ultime notizie. Tutti i settori: politica, cronaca, economia, sport, esteri, scienza, tecnologia, internet, spettacoli, musica, cultura, arte, mostre, libri, dvd, vhs, concerti, cinema, attori, attrici, recensioni, chat, cucina, mappe. Le citta di Repubblica: Roma, Milano, Bologna, Firenze, Palermo, Napoli, Bari, Torino.' # noqa: E501 masthead_url = 'http://www.repubblica.it/static/images/homepage/2010/la-repubblica-logo-home-payoff.png' - publisher = 'Gruppo editoriale L\'Espresso' + publisher = "Gruppo editoriale L'Espresso" category = 'News, politics, culture, economy, general interest' language = 'it' timefmt = '[%a, %d %b, %Y]' @@ -28,9 +28,9 @@ class LaRepubblica(BasicNewsRecipe): publication_type = 'newspaper' articles_are_obfuscated = True temp_files = [] - extra_css = """ + extra_css = ''' img{display: block} - """ + ''' remove_attributes = ['width', 'height', 'lang', 'xmlns:og', 'xmlns:fb'] @@ -50,7 +50,7 @@ class LaRepubblica(BasicNewsRecipe): html = response.read() count = 10 except: - print("Retrying download...") + print('Retrying download...') count += 1 self.temp_files.append(PersistentTemporaryFile('_fa.html')) self.temp_files[-1].write(html) diff --git a/recipes/lalibre_be.recipe b/recipes/lalibre_be.recipe index cef8318a0e..100c32712a 100644 --- a/recipes/lalibre_be.recipe +++ b/recipes/lalibre_be.recipe @@ -32,7 +32,7 @@ class LaLibre(BasicNewsRecipe): feeds = [ - (u'L\'actu', u'http://www.lalibre.be/rss/?section=10'), + (u"L'actu", u'http://www.lalibre.be/rss/?section=10'), (u'Culture', u'http://www.lalibre.be/rss/?section=5'), (u'Economie', u'http://www.lalibre.be/rss/?section=3'), (u'Libre Entreprise', u'http://www.lalibre.be/rss/?section=904'), diff --git a/recipes/lanacion.recipe b/recipes/lanacion.recipe index a8290a0337..326b9a575f 100644 --- a/recipes/lanacion.recipe +++ b/recipes/lanacion.recipe @@ -9,7 +9,7 @@ from calibre.web.feeds.news import BasicNewsRecipe class Lanacion(BasicNewsRecipe): title = 'La Nacion' __author__ = 'Darko Miletic' - description = "lanacion.com - Informacion actualizada las 24 horas, con noticias de Argentina y del mundo" + description = 'lanacion.com - Informacion actualizada las 24 horas, con noticias de Argentina y del mundo' publisher = 'La Nacion S.A.' category = 'news, politics, Argentina' oldest_article = 1 @@ -22,13 +22,13 @@ class Lanacion(BasicNewsRecipe): publication_type = 'newspaper' remove_empty_feeds = True masthead_url = 'http://www.lanacion.com.ar/_ui/desktop/imgs/layout/logos/ln-home.gif' - extra_css = """ + extra_css = ''' h1{font-family: TheSans,Arial,sans-serif} body{font-family: Arial,sans-serif} img{display: block} .firma,.fecha{font-size: small} .epigrafe-columna{font-size: x-small} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/lapoliticaonline_ar.recipe b/recipes/lapoliticaonline_ar.recipe index 7134b6dcf6..aa1e78310d 100644 --- a/recipes/lapoliticaonline_ar.recipe +++ b/recipes/lapoliticaonline_ar.recipe @@ -9,7 +9,7 @@ from calibre.web.feeds.news import BasicNewsRecipe class LaPoliticaOnline_AR(BasicNewsRecipe): title = 'La Politica Online' __author__ = 'Darko Miletic' - description = "Informacion actualizada las 24 horas, con noticias de Argentina y del mundo" + description = 'Informacion actualizada las 24 horas, con noticias de Argentina y del mundo' publisher = 'La Politica Online SA' category = 'news, politics, Argentina' oldest_article = 1 @@ -22,13 +22,13 @@ class LaPoliticaOnline_AR(BasicNewsRecipe): publication_type = 'newspaper' remove_empty_feeds = True masthead_url = 'http://www.lapoliticaonline.com/0/img/header/logo.gif' - extra_css = """ + extra_css = ''' .title,.vsmcontent{font-family: Georgia,"Times New Roman",Times,serif} body{font-family: Arial,Helvetica,sans-serif} .galleryfooter{font-size: small; color: gainsboro;} img{display: block} .title{font-size: x-large; font-weight: bold; line-height: 2em;} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/laprensa.recipe b/recipes/laprensa.recipe index 820edf8abe..bf04fa9dfa 100644 --- a/recipes/laprensa.recipe +++ b/recipes/laprensa.recipe @@ -35,33 +35,33 @@ class LaPrensa(BasicNewsRecipe): filter_regexps = [r'.*archive.aspx.*'] remove_tags = [ - dict(name='td', attrs={'class': ["link-registro", "link-buscador"]}), + dict(name='td', attrs={'class': ['link-registro', 'link-buscador']}), dict(name='td', attrs={ - 'id': ["TDTabItem1", "TDTabItem2", "TDTabItem3", "TDTabItem4"]}), - dict(name='table', attrs={'class': ["marco-botonera"]}), - dict(name='tr', attrs={'class': ["messages", "IUTabItemSelected"]}), - dict(name='input', attrs={'id': "txt_allfields"}), + 'id': ['TDTabItem1', 'TDTabItem2', 'TDTabItem3', 'TDTabItem4']}), + dict(name='table', attrs={'class': ['marco-botonera']}), + dict(name='tr', attrs={'class': ['messages', 'IUTabItemSelected']}), + dict(name='input', attrs={'id': 'txt_allfields'}), dict(name='div', attrs={ - 'id': ["TabItem1", "TabItem2", "TabItem3", "TabItem4", "RCPanel"]}), - dict(name='span', attrs={'id': ["GWCNavigatorControl", "_ctl15"]}), - dict(name='span', attrs={'class': ["ranking-titulo", "IUTab"]}), - dict(name='a', attrs={'class': ["link-registro", ]}), - dict(name='img', src="/versions/1/imgs/icono-comentario.gif"), - dict(name='img', src="/versions/1/imgs/logo.gif"), - dict(name='img', src="/versions/1/imgs/boton-ingresar-roll.gif"), - dict(name='img', src="/versions/1/imgs/icono-recomendar.gif"), + 'id': ['TabItem1', 'TabItem2', 'TabItem3', 'TabItem4', 'RCPanel']}), + dict(name='span', attrs={'id': ['GWCNavigatorControl', '_ctl15']}), + dict(name='span', attrs={'class': ['ranking-titulo', 'IUTab']}), + dict(name='a', attrs={'class': ['link-registro', ]}), + dict(name='img', src='/versions/1/imgs/icono-comentario.gif'), + dict(name='img', src='/versions/1/imgs/logo.gif'), + dict(name='img', src='/versions/1/imgs/boton-ingresar-roll.gif'), + dict(name='img', src='/versions/1/imgs/icono-recomendar.gif'), dict(name='button'), - dict(name='img', src="/versions/1/imgs/boton-votar-roll.gif"), - dict(name='img', src="/versions/1/imgs/boton-ingresar.gif"), - dict(name='img', src="/versions/1/imgs/icono-imprimir.gif"), - dict(name='img', src="/versions/1/imgs/icono-ampliar-letra.gif"), - dict(name='img', src="/versions/1/imgs/icono-reducir-letra.gif"), - dict(name='img', src="/versions/1/imgs/pix-trans.gif"), - dict(name='img', src="/versions/1/imgs/icono-buscador.gif"), - dict(name='img', src="/versions/1/imgs/separador-linea-azul.gif"), - dict(name='img', src=" /versions/1/imgs/separador-linea.gif"), - dict(name='a', text="Powered by Civinext Groupware - V. 2.0.3567.23706"), - dict(name='img', height="0") + dict(name='img', src='/versions/1/imgs/boton-votar-roll.gif'), + dict(name='img', src='/versions/1/imgs/boton-ingresar.gif'), + dict(name='img', src='/versions/1/imgs/icono-imprimir.gif'), + dict(name='img', src='/versions/1/imgs/icono-ampliar-letra.gif'), + dict(name='img', src='/versions/1/imgs/icono-reducir-letra.gif'), + dict(name='img', src='/versions/1/imgs/pix-trans.gif'), + dict(name='img', src='/versions/1/imgs/icono-buscador.gif'), + dict(name='img', src='/versions/1/imgs/separador-linea-azul.gif'), + dict(name='img', src=' /versions/1/imgs/separador-linea.gif'), + dict(name='a', text='Powered by Civinext Groupware - V. 2.0.3567.23706'), + dict(name='img', height='0') ] extra_css = ''' @@ -95,8 +95,8 @@ class LaPrensa(BasicNewsRecipe): soup.head.insert(0, mtag) for item in soup.findAll(style=True): del item['style'] - for item in soup.findAll(align="center"): + for item in soup.findAll(align='center'): del item['align'] - for item in soup.findAll(bgcolor="ffffff"): + for item in soup.findAll(bgcolor='ffffff'): del item['bgcolor'] return soup diff --git a/recipes/le_canard_enchaine.recipe b/recipes/le_canard_enchaine.recipe index 2da45db09f..a4ecd4862d 100644 --- a/recipes/le_canard_enchaine.recipe +++ b/recipes/le_canard_enchaine.recipe @@ -61,7 +61,7 @@ class LeCanardEnchaine(BasicNewsRecipe): elif img and img.get('src'): return 'https://boutique.lecanardenchaine.fr' + img['src'] - self.log.info('Aucune couverture trouvée, utilisation de l\'image par défaut') + self.log.info("Aucune couverture trouvée, utilisation de l'image par défaut") return 'https://image.ausha.co/2x1H3rkhwjmSwAa8KzIFfcN0G9GxfJWY83UafXn8_400x400.jpeg' except Exception: self.log.exception('Erreur lors de la récupération de la couverture') @@ -90,7 +90,7 @@ class LeCanardEnchaine(BasicNewsRecipe): feeds = [] for section_title, section_url in self.SECTIONS.items(): - print(f"Exploration de la rubrique : {section_title}") + print(f'Exploration de la rubrique : {section_title}') articles = [] try: url = 'https://www.lecanardenchaine.fr' + section_url @@ -119,10 +119,10 @@ class LeCanardEnchaine(BasicNewsRecipe): if unique_articles: feeds.append((section_title, unique_articles)) - print(f" {len(unique_articles)} articles trouvés") + print(f' {len(unique_articles)} articles trouvés') except Exception as e: - print(f"Erreur sur {section_title}: {str(e)}") + print(f'Erreur sur {section_title}: {str(e)}') return feeds diff --git a/recipes/le_gorafi.recipe b/recipes/le_gorafi.recipe index ee3adcecce..494fba7c0c 100644 --- a/recipes/le_gorafi.recipe +++ b/recipes/le_gorafi.recipe @@ -13,7 +13,7 @@ from calibre.web.feeds.news import BasicNewsRecipe class LeGorafi(BasicNewsRecipe): title = u'Le GORAFI.fr' __author__ = 'Malah, LAntoine' - description = u'Depuis 1826, toute l\'information de sources contradictoires' + description = u"Depuis 1826, toute l'information de sources contradictoires" oldest_article = 7 language = 'fr' max_articles_per_feed = 100 @@ -54,7 +54,7 @@ class LeGorafi(BasicNewsRecipe): soup = self.index_to_soup(article.url) img = soup.select_one('#mvp-post-feat-img img') return img['data-lazy-src'] - print("No cover found") + print('No cover found') return None def parse_feeds(self): diff --git a/recipes/le_monde_diplomatique_fr.recipe b/recipes/le_monde_diplomatique_fr.recipe index d2804a49a1..a07e9f78bf 100644 --- a/recipes/le_monde_diplomatique_fr.recipe +++ b/recipes/le_monde_diplomatique_fr.recipe @@ -104,7 +104,7 @@ class LeMondeDiplomatiqueSiteWeb(BasicNewsRecipe): 'url': absurl(feed_link['href']), 'description': description }) - return [("La valise diplomatique", articles)] + return [('La valise diplomatique', articles)] def parse_index_cartes(self): articles = [] @@ -125,7 +125,7 @@ class LeMondeDiplomatiqueSiteWeb(BasicNewsRecipe): 'url': absurl(feed_link['href']), 'description': author }) - return [("Cartes", articles)] + return [('Cartes', articles)] def parse_feeds(self): feeds = BasicNewsRecipe.parse_feeds(self) diff --git a/recipes/le_monde_sub_paper.recipe b/recipes/le_monde_sub_paper.recipe index 0b6a7de449..46b366a4d0 100644 --- a/recipes/le_monde_sub_paper.recipe +++ b/recipes/le_monde_sub_paper.recipe @@ -51,7 +51,7 @@ class LeMondeAbonne(BasicNewsRecipe): zipurl_format = 'http://medias.lemonde.fr/abonnes/editionelectronique/%Y%m%d/html/%y%m%d.zip' coverurl_format = '/img/%y%m%d01.jpg' masthead_url = 'http://upload.wikimedia.org/wikipedia/commons/thumb/5/54/Le_monde_logo.svg/800px-Le_monde_logo.svg.png' - path_format = "%y%m%d" + path_format = '%y%m%d' keep_only_tags = [ dict(name=['h1']), @@ -66,7 +66,7 @@ class LeMondeAbonne(BasicNewsRecipe): dict(name='div', attrs={'class': 'po-copy'}) ] - article_id_pattern = re.compile("[0-9]+\\.html") + article_id_pattern = re.compile('[0-9]+\\.html') article_url_format = 'http://www.lemonde.fr/journalelectronique/donnees/protege/%Y%m%d/html/' def get_browser(self): @@ -92,7 +92,7 @@ class LeMondeAbonne(BasicNewsRecipe): for i in range(7): self.ltime = time.gmtime(second) - self.timefmt = time.strftime(" %A %d %B %Y", + self.timefmt = time.strftime(' %A %d %B %Y', self.ltime).decode(preferred_encoding) url = time.strftime(self.zipurl_format, self.ltime) try: @@ -113,7 +113,7 @@ class LeMondeAbonne(BasicNewsRecipe): zfile.close() path = os.path.join( - self.output_dir, time.strftime(self.path_format, self.ltime), "data" + self.output_dir, time.strftime(self.path_format, self.ltime), 'data' ) self.articles_path = path @@ -121,7 +121,7 @@ class LeMondeAbonne(BasicNewsRecipe): files = os.listdir(path) nb_index_files = len([ - name for name in files if re.match("frame_gauche_[0-9]+.html", name) + name for name in files if re.match('frame_gauche_[0-9]+.html', name) ]) flux = [] @@ -129,39 +129,39 @@ class LeMondeAbonne(BasicNewsRecipe): article_url = time.strftime(self.article_url_format, self.ltime) for i in range(nb_index_files): - filename = os.path.join(path, "selection_%d.html" % (i + 1)) + filename = os.path.join(path, 'selection_%d.html' % (i + 1)) with open(filename, 'rb') as tmp: soup = self.index_to_soup(tmp.read()) title = soup.find('span').contents[0] - if title == "Une": - title = "À la une" - if title == "Evenement": + if title == 'Une': + title = 'À la une' + if title == 'Evenement': title = "L'événement" - if title == "Planete": - title = "Planète" - if title == "Economie - Entreprises": - title = "Économie" + if title == 'Planete': + title = 'Planète' + if title == 'Economie - Entreprises': + title = 'Économie' if title == "L'Oeil du Monde": title = "L'œil du Monde" - if title == "Enquete": - title = "Enquête" - if title == "Editorial - Analyses": - title = "Horizons" - if title == "Le Monde Economie": - title = "Économie" - if title == "Lettre et chronique": - title = "Idées" - if title == "Le Monde Géo et politique": - title = "Géopolitique" - if title == "Météo - Jeux - Ecrans": - title = "Économie & Entreprise" + if title == 'Enquete': + title = 'Enquête' + if title == 'Editorial - Analyses': + title = 'Horizons' + if title == 'Le Monde Economie': + title = 'Économie' + if title == 'Lettre et chronique': + title = 'Idées' + if title == 'Le Monde Géo et politique': + title = 'Géopolitique' + if title == 'Météo - Jeux - Ecrans': + title = 'Économie & Entreprise' tmp.close() - filename = os.path.join(path, "frame_gauche_%d.html" % (i + 1)) + filename = os.path.join(path, 'frame_gauche_%d.html' % (i + 1)) with open(filename, 'rb') as tmp: soup = self.index_to_soup(tmp.read()) articles = [] - for link in soup.findAll("a"): + for link in soup.findAll('a'): article_file = link['href'] article_id = self.article_id_pattern.search(article_file).group() article = { diff --git a/recipes/le_peuple_breton.recipe b/recipes/le_peuple_breton.recipe index cad05cc0c5..ce6e126c26 100644 --- a/recipes/le_peuple_breton.recipe +++ b/recipes/le_peuple_breton.recipe @@ -10,7 +10,7 @@ from calibre.web.feeds.news import BasicNewsRecipe class LePeupleBreton(BasicNewsRecipe): title = 'Le Peuple Breton' __author__ = 'Lionel Plais' - description = u'Aujourd\'hui, être libre c\'est être informé' + description = u"Aujourd'hui, être libre c'est être informé" oldest_article = 90 language = 'fr' cover_img_url = 'http://lepeuplebreton.bzh/wp-content/uploads/2017/11/le-peuple-breton-logo.jpg' diff --git a/recipes/leggo_it.recipe b/recipes/leggo_it.recipe index 2ad79b1db8..bb8a6176a9 100644 --- a/recipes/leggo_it.recipe +++ b/recipes/leggo_it.recipe @@ -53,8 +53,8 @@ class LeggoIT(BasicNewsRecipe): cover = None st = time.localtime() year = str(st.tm_year) - month = "%.2d" % st.tm_mon - day = "%.2d" % st.tm_mday + month = '%.2d' % st.tm_mon + day = '%.2d' % st.tm_mday cover = 'http://www.leggo.it/' + year + month + day + '/jpeg/LEGGO_ROMA_1.jpg' br = BasicNewsRecipe.get_browser(self) try: @@ -65,6 +65,6 @@ class LeggoIT(BasicNewsRecipe): try: br.open(cover) except: - self.log("\nCover unavailable") + self.log('\nCover unavailable') cover = 'http://www.leggo.it/img/logo-leggo2.gif' return cover diff --git a/recipes/lemonde_dip.recipe b/recipes/lemonde_dip.recipe index 2563c5cd1e..bc2f6614d4 100644 --- a/recipes/lemonde_dip.recipe +++ b/recipes/lemonde_dip.recipe @@ -30,13 +30,13 @@ class LeMondeDiplomatiqueEn(BasicNewsRecipe): INDEX = PREFIX + strftime('%Y/%m/') use_embedded_content = False language = 'en' - extra_css = """ + extra_css = ''' body{font-family: "Luxi sans","Lucida sans","Lucida Grande",Lucida,"Lucida Sans Unicode",sans-serif} .surtitre{font-size: 1.2em; font-variant: small-caps; margin-bottom: 0.5em} .chapo{font-size: 1.2em; font-weight: bold; margin: 1em 0 0.5em} .texte{font-family: Georgia,"Times New Roman",serif} h1{color: #990000} .notes{border-top: 1px solid #CCCCCC; font-size: 0.9em; line-height: 1.4em} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/lepoint.recipe b/recipes/lepoint.recipe index 47a59d7994..db8bfe6165 100644 --- a/recipes/lepoint.recipe +++ b/recipes/lepoint.recipe @@ -71,6 +71,6 @@ class lepoint(BasicNewsRecipe): try: br.open(masthead) except: - self.log("\nCover unavailable") + self.log('\nCover unavailable') masthead = None return masthead diff --git a/recipes/lexpress.recipe b/recipes/lexpress.recipe index 773c22818f..a8ba0a30b0 100644 --- a/recipes/lexpress.recipe +++ b/recipes/lexpress.recipe @@ -15,7 +15,7 @@ def classes(classes): class lepoint(BasicNewsRecipe): - title = 'L\'express' + title = "L'express" __author__ = 'calibre' description = 'Actualités' publisher = 'LExpress.fr' @@ -73,6 +73,6 @@ class lepoint(BasicNewsRecipe): try: br.open(masthead) except: - self.log("\nCover unavailable") + self.log('\nCover unavailable') masthead = None return masthead diff --git a/recipes/liberation.recipe b/recipes/liberation.recipe index c0f9702986..531b02a41f 100644 --- a/recipes/liberation.recipe +++ b/recipes/liberation.recipe @@ -78,9 +78,9 @@ class Liberation(BasicNewsRecipe): title = 'Libération' __author__ = 'unkn0wn' description = ( - 'Libération est un quotidien d\'information libre, vigilant et engagé. L\'objectif de Libération est de ' + "Libération est un quotidien d'information libre, vigilant et engagé. L'objectif de Libération est de " 'fournir une information complète et vérifiée, dans tous les domaines. Sans préjugés, ni complaisance, ' - 'ses enquêtes reportages et analyses s\'emploient à comprendre et à décrire l\'actualité et à révéler ' + "ses enquêtes reportages et analyses s'emploient à comprendre et à décrire l'actualité et à révéler " 'les mutations des sociétés et des cultures.' ) language = 'fr' diff --git a/recipes/libertad_digital.recipe b/recipes/libertad_digital.recipe index 55a90adba3..b3a571345b 100644 --- a/recipes/libertad_digital.recipe +++ b/recipes/libertad_digital.recipe @@ -22,10 +22,10 @@ class LibertadDigital(BasicNewsRecipe): remove_empty_feeds = True publication_type = 'website' masthead_url = 'http://s.libertaddigital.com/images/logo.gif' - extra_css = """ + extra_css = ''' body{font-family: Verdana,sans-serif } img{margin-bottom: 0.4em; display:block} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/livemint.recipe b/recipes/livemint.recipe index 8cdbbf8bec..90c8e5a05b 100644 --- a/recipes/livemint.recipe +++ b/recipes/livemint.recipe @@ -42,7 +42,7 @@ class LiveMint(BasicNewsRecipe): if 'MINT_FRONT_1' in x['src']: return 'https://epaper.livemint.com' + x['src'].replace('-S', '') - extra_css = """ + extra_css = ''' img {margin:0 auto;} .psTopLogoItem img, .ecologoStory { width:100; } #img-cap {font-size:small; text-align:center;} @@ -51,7 +51,7 @@ class LiveMint(BasicNewsRecipe): } em, blockquote {color:#202020;} .moreAbout, .articleInfo, .metaData, .psTopicsHeading, .topicsTag, .auth {font-size:small;} - """ + ''' keep_only_tags = [ dict( diff --git a/recipes/livescience.recipe b/recipes/livescience.recipe index 2cc26e321e..5506d15fc7 100644 --- a/recipes/livescience.recipe +++ b/recipes/livescience.recipe @@ -5,8 +5,8 @@ from calibre.web.feeds.news import BasicNewsRecipe class LiveScience(BasicNewsRecipe): - title = "Live Science" - description = "For the science geek in everyone! Stories on the latest findings from science journals and institutions. Sourced from livescience.com" + title = 'Live Science' + description = 'For the science geek in everyone! Stories on the latest findings from science journals and institutions. Sourced from livescience.com' __author__ = 'yodha8' language = 'en' oldest_article = 7 diff --git a/recipes/lwn_free.recipe b/recipes/lwn_free.recipe index 0a9c1d9993..7cc7c473a4 100644 --- a/recipes/lwn_free.recipe +++ b/recipes/lwn_free.recipe @@ -5,7 +5,7 @@ from calibre.web.feeds.news import BasicNewsRecipe class LWNFree(BasicNewsRecipe): - title = "LWN Linux Weekly News (Free)" + title = 'LWN Linux Weekly News (Free)' language = 'en' __author__ = 'yodha8' description = "LWN is published every Thursday. This recipe skips current week's articles (subscriber-only) and pulls free articles from previous week." @@ -18,7 +18,7 @@ class LWNFree(BasicNewsRecipe): ] def parse_feeds(self): - """Remove paid articles and articles older than a week.""" + '''Remove paid articles and articles older than a week.''' prev_feeds = super().parse_feeds() @@ -28,12 +28,12 @@ class LWNFree(BasicNewsRecipe): for article in prev_feeds[0]: # Paid article - if "[$]" in article.title: + if '[$]' in article.title: remove_articles.append(article) continue # Count how many free weekly edition we passed - if "Weekly Edition" in article.title: + if 'Weekly Edition' in article.title: weekly_count += 1 # Remove all articles starting from 2nd free weekly edition diff --git a/recipes/lwn_weekly.recipe b/recipes/lwn_weekly.recipe index 8917cbc423..6867f22ccb 100644 --- a/recipes/lwn_weekly.recipe +++ b/recipes/lwn_weekly.recipe @@ -137,7 +137,7 @@ class WeeklyLWN(BasicNewsRecipe): article_title = _('Undefined article title') if subsection: - section_title = "%s: %s" % (section, subsection) + section_title = '%s: %s' % (section, subsection) else: section_title = section @@ -170,7 +170,7 @@ class WeeklyLWN(BasicNewsRecipe): }) else: - self.log.error("lwn_weekly.recipe: something bad happened; should not be able to reach this") + self.log.error('lwn_weekly.recipe: something bad happened; should not be able to reach this') ans = [(section2, articles[section2]) for section2 in ans if section2 in articles] diff --git a/recipes/mainichi.recipe b/recipes/mainichi.recipe index 69c0159996..4eaa4f02e5 100644 --- a/recipes/mainichi.recipe +++ b/recipes/mainichi.recipe @@ -1,7 +1,7 @@ #!/usr/bin/env python -""" +''' www.mainichi.jp -""" +''' from calibre.web.feeds.news import BasicNewsRecipe @@ -9,11 +9,11 @@ from calibre.web.feeds.news import BasicNewsRecipe class MainichiDailyNews(BasicNewsRecipe): title = u'\u6bce\u65e5\u65b0\u805e' __author__ = 'unkn0wn' - description = "Japanese traditional newspaper Mainichi Daily News" - publisher = "Mainichi News" - publication_type = "newspaper" - category = "news, japan" - language = "ja" + description = 'Japanese traditional newspaper Mainichi Daily News' + publisher = 'Mainichi News' + publication_type = 'newspaper' + category = 'news, japan' + language = 'ja' no_stylesheets = True remove_javascript = True diff --git a/recipes/mainichi_en.recipe b/recipes/mainichi_en.recipe index ec74c962df..6f083554e3 100644 --- a/recipes/mainichi_en.recipe +++ b/recipes/mainichi_en.recipe @@ -1,23 +1,23 @@ #!/usr/bin/env python -""" +''' www.mainichi.jp/english -""" +''' from calibre.web.feeds.news import BasicNewsRecipe class MainichiEnglishNews(BasicNewsRecipe): - title = u"The Mainichi" + title = u'The Mainichi' __author__ = 'unkn0wn' - description = "Japanese traditional newspaper Mainichi news in English" - publisher = "Mainichi News" - publication_type = "newspaper" - category = "news, japan" - language = "en_JP" + description = 'Japanese traditional newspaper Mainichi news in English' + publisher = 'Mainichi News' + publication_type = 'newspaper' + category = 'news, japan' + language = 'en_JP' - index = "http://mainichi.jp/english/" - masthead_url = index + "images/themainichi.png" + index = 'http://mainichi.jp/english/' + masthead_url = index + 'images/themainichi.png' no_stylesheets = True remove_javascript = True diff --git a/recipes/mainichi_science_news.recipe b/recipes/mainichi_science_news.recipe index d7ede8543b..381e145107 100644 --- a/recipes/mainichi_science_news.recipe +++ b/recipes/mainichi_science_news.recipe @@ -22,8 +22,8 @@ class MainichiDailyScienceNews(BasicNewsRecipe): remove_javascript = True masthead_title = u'MAINICHI DAILY NEWS' - remove_tags_before = {'class': "NewsTitle"} - remove_tags_after = {'class': "NewsBody clr"} + remove_tags_before = {'class': 'NewsTitle'} + remove_tags_after = {'class': 'NewsBody clr'} def parse_feeds(self): diff --git a/recipes/marca.recipe b/recipes/marca.recipe index fd64c937f9..0235ce8ff4 100644 --- a/recipes/marca.recipe +++ b/recipes/marca.recipe @@ -23,12 +23,12 @@ class Marca(BasicNewsRecipe): language = 'es' publication_type = 'newsportal' masthead_url = 'http://estaticos.marca.com/deporte/img/v3.0/img_marca-com.png' - extra_css = """ + extra_css = ''' body{font-family: Tahoma,Geneva,sans-serif} h1,h2,h3,h4,h5,h6{font-family: 'LatoBlack',Tahoma,Geneva,sans-serif} .cab_articulo h4 {font-family: Georgia,"Times New Roman",Times,serif} .antetitulo{text-transform: uppercase} - """ + ''' feeds = [(u'Portada', u'http://estaticos.marca.com/rss/portada.xml')] diff --git a/recipes/marctv.recipe b/recipes/marctv.recipe index ca5cbe5f84..87890a95a9 100644 --- a/recipes/marctv.recipe +++ b/recipes/marctv.recipe @@ -26,7 +26,7 @@ class MarcTVde(BasicNewsRecipe): remove_tags = [] - keep_only_tags = dict(name='div', attrs={'class': ["content"]}) + keep_only_tags = dict(name='div', attrs={'class': ['content']}) feeds = [ (u'Spiele', u'http://feeds.feedburner.com/marctv/spiele'), diff --git a/recipes/mediaindonesia.recipe b/recipes/mediaindonesia.recipe index e282ac450d..4f5dd5b740 100644 --- a/recipes/mediaindonesia.recipe +++ b/recipes/mediaindonesia.recipe @@ -24,7 +24,7 @@ class Media(BasicNewsRecipe): no_javascript = True remove_tags = [dict(id=['atas', 'merah', 'putih']), dict(name='a')] - remove_tags_after = [dict(id="putih")] + remove_tags_after = [dict(id='putih')] extra_css = ''' .judul {font-size: x-large;} diff --git a/recipes/mediapart.recipe b/recipes/mediapart.recipe index 67e97aeb6e..166cea0044 100644 --- a/recipes/mediapart.recipe +++ b/recipes/mediapart.recipe @@ -49,7 +49,7 @@ class Mediapart(BasicNewsRecipe): conversion_options = {'smarten_punctuation': True} - masthead_url = "https://raw.githubusercontent.com/lhoupert/calibre_contrib/main/mediapart_masthead.png" + masthead_url = 'https://raw.githubusercontent.com/lhoupert/calibre_contrib/main/mediapart_masthead.png' ignore_duplicate_articles = {'title'} resolve_internal_links = True diff --git a/recipes/merco_press.recipe b/recipes/merco_press.recipe index 88d1eaaaa5..77433a188c 100644 --- a/recipes/merco_press.recipe +++ b/recipes/merco_press.recipe @@ -3,7 +3,7 @@ from calibre.web.feeds.news import BasicNewsRecipe class MercoPress(BasicNewsRecipe): title = u'Merco Press' - description = u"Read News, Stories and Insight Analysis from Latin America and Mercosur. Politics, Economy, Business and Investments in South America." + description = u'Read News, Stories and Insight Analysis from Latin America and Mercosur. Politics, Economy, Business and Investments in South America.' cover_url = 'http://en.mercopress.com/web/img/en/mercopress-logo.gif' __author__ = 'Russell Phillips' diff --git a/recipes/mit_technology_review.recipe b/recipes/mit_technology_review.recipe index dc14af12e2..1bc7b4d58c 100644 --- a/recipes/mit_technology_review.recipe +++ b/recipes/mit_technology_review.recipe @@ -19,7 +19,7 @@ def absurl(x): if x.startswith('//'): x = 'http:' + x elif not x.startswith('http'): - x = "http://www.technologyreview.com" + x + x = 'http://www.technologyreview.com' + x return x @@ -58,8 +58,8 @@ class MitTechnologyReview(BasicNewsRecipe): prefixed_classes('contentHeader contentArticleHeader contentBody') ] remove_tags = [ - dict(name="aside"), - dict(name="svg"), + dict(name='aside'), + dict(name='svg'), prefixed_classes( 'image__placeholder sliderAd__wrapper eyebrow__wrap-- screen-reader-text' ), @@ -83,7 +83,7 @@ class MitTechnologyReview(BasicNewsRecipe): if script := soup.find('script', id='preload'): raw = script.contents[0] m = re.search(r'\"children\":\[{\"name\":\"magazine-hero\"', raw) - spl = re.split(r"(?=\{)", raw[m.start():], 1)[1] + spl = re.split(r'(?=\{)', raw[m.start():], 1)[1] data = json.JSONDecoder().raw_decode(spl)[0] self.cover_url = data['children'][0]['config']['src'] + '?fit=572,786' self.timefmt = ' [' + data['config']['issueDate'] + ']' @@ -94,7 +94,7 @@ class MitTechnologyReview(BasicNewsRecipe): feeds = OrderedDict() classNamePrefixes = [ - "magazineHero__letter--", "teaserItem__title", "teaserItem--aside__title" + 'magazineHero__letter--', 'teaserItem__title', 'teaserItem--aside__title' ] for div in soup.findAll( attrs={ diff --git a/recipes/mmc_rtv.recipe b/recipes/mmc_rtv.recipe index 67bc888d90..d39266c3e5 100644 --- a/recipes/mmc_rtv.recipe +++ b/recipes/mmc_rtv.recipe @@ -10,7 +10,7 @@ from calibre.web.feeds.news import BasicNewsRecipe class MMCRTV(BasicNewsRecipe): title = u'MMC RTV Slovenija' __author__ = u'TadejS' - description = u"Prvi interaktivni multimedijski portal, MMC RTV Slovenija" + description = u'Prvi interaktivni multimedijski portal, MMC RTV Slovenija' oldest_article = 3 max_articles_per_feed = 100 language = 'sl' diff --git a/recipes/modoros.recipe b/recipes/modoros.recipe index a934eb8427..a65499f08c 100644 --- a/recipes/modoros.recipe +++ b/recipes/modoros.recipe @@ -15,7 +15,7 @@ from hashlib import md5 class ModorosBlogHu(BasicNewsRecipe): __author__ = 'Zsolt Botykai' title = u'Modoros Blog' - description = u"Modoros.blog.hu" + description = u'Modoros.blog.hu' oldest_article = 10000 max_articles_per_feed = 10000 reverse_article_order = True diff --git a/recipes/montreal_gazette.recipe b/recipes/montreal_gazette.recipe index 6701af2093..cc3633e2a2 100644 --- a/recipes/montreal_gazette.recipe +++ b/recipes/montreal_gazette.recipe @@ -164,24 +164,24 @@ class CanWestPaper(BasicNewsRecipe): continue break if daysback == 7: - self.log("\nCover unavailable") + self.log('\nCover unavailable') cover = None return cover def fixChars(self, string): # Replace lsquo (\x91) - fixed = re.sub("\x91", "‘", string) + fixed = re.sub('\x91', '‘', string) # Replace rsquo (\x92) - fixed = re.sub("\x92", "’", fixed) + fixed = re.sub('\x92', '’', fixed) # Replace ldquo (\x93) - fixed = re.sub("\x93", "“", fixed) + fixed = re.sub('\x93', '“', fixed) # Replace rdquo (\x94) - fixed = re.sub("\x94", "”", fixed) + fixed = re.sub('\x94', '”', fixed) # Replace ndash (\x96) - fixed = re.sub("\x96", "–", fixed) + fixed = re.sub('\x96', '–', fixed) # Replace mdash (\x97) - fixed = re.sub("\x97", "—", fixed) - fixed = re.sub("’", "’", fixed) + fixed = re.sub('\x97', '—', fixed) + fixed = re.sub('’', '’', fixed) return fixed def massageNCXText(self, description): @@ -262,10 +262,10 @@ class CanWestPaper(BasicNewsRecipe): if url.startswith('/'): url = self.url_prefix + url if not url.startswith(self.url_prefix): - print("Rejected " + url) + print('Rejected ' + url) return if url in self.url_list: - print("Rejected dup " + url) + print('Rejected dup ' + url) return self.url_list.append(url) title = self.tag_to_string(atag, False) @@ -277,8 +277,8 @@ class CanWestPaper(BasicNewsRecipe): return dtag = adiv.find('div', 'content') description = '' - print("URL " + url) - print("TITLE " + title) + print('URL ' + url) + print('TITLE ' + title) if dtag is not None: stag = dtag.span if stag is not None: @@ -286,18 +286,18 @@ class CanWestPaper(BasicNewsRecipe): description = self.tag_to_string(stag, False) else: description = self.tag_to_string(dtag, False) - print("DESCRIPTION: " + description) + print('DESCRIPTION: ' + description) if key not in articles: articles[key] = [] articles[key].append(dict( title=title, url=url, date='', description=description, author='', content='')) def parse_web_index(key, keyurl): - print("Section: " + key + ': ' + self.url_prefix + keyurl) + print('Section: ' + key + ': ' + self.url_prefix + keyurl) try: soup = self.index_to_soup(self.url_prefix + keyurl) except: - print("Section: " + key + ' NOT FOUND') + print('Section: ' + key + ' NOT FOUND') return ans.append(key) mainsoup = soup.find('div', 'bodywrapper') diff --git a/recipes/nacional_cro.recipe b/recipes/nacional_cro.recipe index 997e6903c1..97333d6c9b 100644 --- a/recipes/nacional_cro.recipe +++ b/recipes/nacional_cro.recipe @@ -23,7 +23,7 @@ def new_tag(soup, name, attrs=()): class NacionalCro(BasicNewsRecipe): title = 'Nacional - Hr' __author__ = 'Darko Miletic' - description = "news from Croatia" + description = 'news from Croatia' publisher = 'Nacional.hr' category = 'news, politics, Croatia' oldest_article = 2 @@ -53,9 +53,9 @@ class NacionalCro(BasicNewsRecipe): soup.html['lang'] = self.lang soup.html['dir'] = self.direction mlang = new_tag(soup, 'meta', [ - ("http-equiv", "Content-Language"), ("content", self.lang)]) + ('http-equiv', 'Content-Language'), ('content', self.lang)]) mcharset = new_tag(soup, 'meta', [ - ("http-equiv", "Content-Type"), ("content", "text/html; charset=UTF-8")]) + ('http-equiv', 'Content-Type'), ('content', 'text/html; charset=UTF-8')]) soup.head.insert(0, mlang) soup.head.insert(1, mcharset) for item in soup.findAll(style=True): diff --git a/recipes/natgeo.recipe b/recipes/natgeo.recipe index f2c054aa92..047c23a544 100644 --- a/recipes/natgeo.recipe +++ b/recipes/natgeo.recipe @@ -44,14 +44,14 @@ class NatGeo(BasicNewsRecipe): def preprocess_raw_html(self, raw_html, url): return self.natgeo_parser.extract_html(raw_html) - extra_css = """ + extra_css = ''' blockquote { color:#404040; } .byline, i { font-style:italic; color:#202020; } .cap { font-size:small; } img {display:block; margin:0 auto;} .cred { font-style:italic; font-size:small; color:#404040; } .auth, .time, .sub { font-size:small; color:#5c5c5c; } - """ + ''' def get_cover_url(self): # soup = self.index_to_soup('https://www.nationalgeographic.com/magazine/') diff --git a/recipes/natgeo_kids.recipe b/recipes/natgeo_kids.recipe index 8884f8488d..813c4bce87 100644 --- a/recipes/natgeo_kids.recipe +++ b/recipes/natgeo_kids.recipe @@ -41,14 +41,14 @@ class NatGeo(BasicNewsRecipe): def preprocess_raw_html(self, raw_html, url): return self.natgeo_parser.extract_html(raw_html) - extra_css = """ + extra_css = ''' blockquote { color:#404040; } .byline, i { font-style:italic; color:#202020; } .cap { font-size:small; } img {display:block; margin:0 auto;} .cred { font-style:italic; font-size:small; color:#404040; } .auth, .time, .sub { font-size:small; color:#5c5c5c; } - """ + ''' def parse_index(self): index = 'https://kids.nationalgeographic.com/' diff --git a/recipes/natgeo_traveller.recipe b/recipes/natgeo_traveller.recipe index eed9e5a0c2..8500b2d8ab 100644 --- a/recipes/natgeo_traveller.recipe +++ b/recipes/natgeo_traveller.recipe @@ -44,14 +44,14 @@ class NatGeo(BasicNewsRecipe): def preprocess_raw_html(self, raw_html, url): return self.natgeo_parser.extract_html(raw_html) - extra_css = """ + extra_css = ''' blockquote { color:#404040; } .byline, i { font-style:italic; color:#202020; } .cap { font-size:small; } img {display:block; margin:0 auto;} .cred { font-style:italic; font-size:small; color:#404040; } .auth, .time, .sub { font-size:small; color:#5c5c5c; } - """ + ''' def parse_index(self): pages = [ diff --git a/recipes/natgeohis.recipe b/recipes/natgeohis.recipe index 61012a18e1..bc56c2be3e 100644 --- a/recipes/natgeohis.recipe +++ b/recipes/natgeohis.recipe @@ -43,14 +43,14 @@ class NatGeo(BasicNewsRecipe): def preprocess_raw_html(self, raw_html, url): return self.natgeo_parser.extract_html(raw_html) - extra_css = """ + extra_css = ''' blockquote { color:#404040; } .byline, i { font-style:italic; color:#202020; } .cap { font-size:small; } img {display:block; margin:0 auto;} .cred { font-style:italic; font-size:small; color:#404040; } .auth, .time, .sub { font-size:small; color:#5c5c5c; } - """ + ''' def get_cover_url(self): soup = self.index_to_soup('https://ngsingleissues.nationalgeographic.com/history') diff --git a/recipes/natgeomag.recipe b/recipes/natgeomag.recipe index 2826d8c1a0..d2ec0f3f49 100644 --- a/recipes/natgeomag.recipe +++ b/recipes/natgeomag.recipe @@ -48,14 +48,14 @@ class NatGeo(BasicNewsRecipe): def preprocess_raw_html(self, raw_html, url): return self.natgeo_parser.extract_html(raw_html) - extra_css = """ + extra_css = ''' blockquote { color:#404040; } .byline, i { font-style:italic; color:#202020; } .cap { font-size:small; } img {display:block; margin:0 auto;} .cred { font-style:italic; font-size:small; color:#404040; } .auth, .time, .sub { font-size:small; color:#5c5c5c; } - """ + ''' def parse_index(self): edition = date.today().strftime('%B-%Y') diff --git a/recipes/nature.recipe b/recipes/nature.recipe index 02c70c211a..1484c44903 100644 --- a/recipes/nature.recipe +++ b/recipes/nature.recipe @@ -40,14 +40,14 @@ class Nature(BasicNewsRecipe): no_javascript = True no_stylesheets = True - keep_only_tags = [dict(name="article")] + keep_only_tags = [dict(name='article')] remove_tags = [ classes( - "u-hide-print hide-print c-latest-content__item c-context-bar " - "c-pdf-button__container u-js-hide" + 'u-hide-print hide-print c-latest-content__item c-context-bar ' + 'c-pdf-button__container u-js-hide' ), - dict(name="img", attrs={"class": ["visually-hidden"]}), + dict(name='img', attrs={'class': ['visually-hidden']}), ] def parse_index(self): @@ -56,15 +56,15 @@ class Nature(BasicNewsRecipe): 'img', attrs={'data-test': check_words('issue-cover-image')} )['src'] try: - self.cover_url = re.sub(r"\bw\d+\b", "w1000", self.cover_url) # enlarge cover size resolution + self.cover_url = re.sub(r'\bw\d+\b', 'w1000', self.cover_url) # enlarge cover size resolution except: - """ + ''' failed, img src might have changed, use default width 200 - """ + ''' pass section_tags = soup.find_all( - "section", attrs={"data-container-type": "issue-section-list"} + 'section', attrs={'data-container-type': 'issue-section-list'} ) sections = defaultdict(list) diff --git a/recipes/nautilus.recipe b/recipes/nautilus.recipe index 7ee8788b08..ae310fe111 100644 --- a/recipes/nautilus.recipe +++ b/recipes/nautilus.recipe @@ -1,7 +1,7 @@ #!/usr/bin/env python -""" +''' nautil.us -""" +''' from calibre.web.feeds.news import BasicNewsRecipe, classes @@ -23,14 +23,14 @@ class Nautilus(BasicNewsRecipe): remove_attributes = ['height', 'width'] ignore_duplicate_articles = {'title', 'url'} remove_empty_feeds = True - extra_css = """ + extra_css = ''' .article-list_item-byline{font-size:small;} blockquote{color:#404040; text-align:center;} #fig-c{font-size:small;} em{color:#202020;} .breadcrumb{color:gray; font-size:small;} .article-author{font-size:small;} - """ + ''' recipe_specific_options = { 'days': { diff --git a/recipes/new_scientist.recipe b/recipes/new_scientist.recipe index ccbc2fc73d..0f9c5f06eb 100644 --- a/recipes/new_scientist.recipe +++ b/recipes/new_scientist.recipe @@ -101,7 +101,7 @@ class NewScientist(BasicNewsRecipe): br = BasicNewsRecipe.get_browser(self) if self.username is not None and self.password is not None: def is_login_form(form): - return "action" in form.attrs and form.attrs['action'] == "/login/" + return 'action' in form.attrs and form.attrs['action'] == '/login/' br.open('https://www.newscientist.com/login/') br.select_form(predicate=is_login_form) diff --git a/recipes/new_scientist_mag.recipe b/recipes/new_scientist_mag.recipe index 9aa04e4ecd..49fe63b97d 100644 --- a/recipes/new_scientist_mag.recipe +++ b/recipes/new_scientist_mag.recipe @@ -40,7 +40,7 @@ class NewScientist(BasicNewsRecipe): br = BasicNewsRecipe.get_browser(self) if self.username is not None and self.password is not None: def is_login_form(form): - return "action" in form.attrs and form.attrs['action'] == "/login/" + return 'action' in form.attrs and form.attrs['action'] == '/login/' br.open('https://www.newscientist.com/login/') br.select_form(predicate=is_login_form) diff --git a/recipes/new_statesman.recipe b/recipes/new_statesman.recipe index 99b41b8765..86ecc7b35f 100644 --- a/recipes/new_statesman.recipe +++ b/recipes/new_statesman.recipe @@ -28,10 +28,10 @@ class NewStatesman(BasicNewsRecipe): ignore_duplicate_articles = {'url'} masthead_url = 'https://www.newstatesman.com/sites/all/themes/creative-responsive-theme/images/newstatesman_logo@2x.png' - extra_css = """ + extra_css = ''' body{font-family: serif} img{margin-top:1em; margin-bottom: 1em; display:block} - """ + ''' conversion_options = { 'comment': description, diff --git a/recipes/new_yorker.recipe b/recipes/new_yorker.recipe index 6d8b49f863..b190c6a33c 100644 --- a/recipes/new_yorker.recipe +++ b/recipes/new_yorker.recipe @@ -16,7 +16,7 @@ def absurl(x): class NewYorker(BasicNewsRecipe): - title = "The New Yorker Magazine" + title = 'The New Yorker Magazine' description = "Articles of the week's New Yorker magazine" language = 'en_US' __author__ = 'Kovid Goyal' @@ -99,9 +99,9 @@ class NewYorker(BasicNewsRecipe): self.log('Found cover:', self.cover_url) try: # the src original resolution w_280 was too low, replace w_280 with w_560 - cover_url_width_index = self.cover_url.find("w_") + cover_url_width_index = self.cover_url.find('w_') old_width = self.cover_url[cover_url_width_index:cover_url_width_index+5] - self.cover_url = self.cover_url.replace(old_width, "w_640") + self.cover_url = self.cover_url.replace(old_width, 'w_640') except Exception: self.log('Failed enlarging cover img, using the original one') @@ -126,7 +126,7 @@ class NewYorker(BasicNewsRecipe): if rub: desc = self.tag_to_string(rub) + ' | ' + desc self.log('\t', title, '\n\t', desc, '\n\t\t', url) - feeds_dict[section].append({"title": title, "url": url, "description": desc}) + feeds_dict[section].append({'title': title, 'url': url, 'description': desc}) return feeds_dict.items() diff --git a/recipes/newrepublicmag.recipe b/recipes/newrepublicmag.recipe index 61658d1b1c..8e66503405 100644 --- a/recipes/newrepublicmag.recipe +++ b/recipes/newrepublicmag.recipe @@ -1,6 +1,6 @@ -""" +''' newrepublic.com -""" +''' import json from functools import cmp_to_key from urllib.parse import urlencode, urljoin, urlparse, urlsplit @@ -10,16 +10,16 @@ from calibre.ebooks.BeautifulSoup import BeautifulSoup from calibre.utils.date import parse_date from calibre.web.feeds.news import BasicNewsRecipe -_issue_url = "" # example: https://newrepublic.com/magazine/may-2023 +_issue_url = '' # example: https://newrepublic.com/magazine/may-2023 def sort_section(a, b, sections_sort): try: - a_index = sections_sort.index(a["section"]) + a_index = sections_sort.index(a['section']) except ValueError: a_index = 999 try: - b_index = sections_sort.index(b["section"]) + b_index = sections_sort.index(b['section']) except ValueError: b_index = 999 @@ -27,31 +27,31 @@ def sort_section(a, b, sections_sort): return -1 if a_index > b_index: return 1 - if a["section"] == b["section"]: - return -1 if a["date"] < b["date"] else 1 - return -1 if a["section"] < b["section"] else 1 + if a['section'] == b['section']: + return -1 if a['date'] < b['date'] else 1 + return -1 if a['section'] < b['section'] else 1 class NewRepublicMagazine(BasicNewsRecipe): - title = "The New Republic Magazine" - language = "en" - __author__ = "ping" + title = 'The New Republic Magazine' + language = 'en' + __author__ = 'ping' description = ( - "Founded in 1914, The New Republic is a media organization dedicated to addressing " - "today’s most critical issues. https://newrepublic.com/magazine" + 'Founded in 1914, The New Republic is a media organization dedicated to addressing ' + 'today’s most critical issues. https://newrepublic.com/magazine' ) - publication_type = "magazine" + publication_type = 'magazine' use_embedded_content = False - masthead_url = "https://images.newrepublic.com/f5acdc0030e3212e601040dd24d5c2c0c684b15f.png?w=512&q=65&dpi=1&fit=crop&crop=faces&h=256" - remove_attributes = ["height", "width"] - ignore_duplicate_articles = {"title", "url"} + masthead_url = 'https://images.newrepublic.com/f5acdc0030e3212e601040dd24d5c2c0c684b15f.png?w=512&q=65&dpi=1&fit=crop&crop=faces&h=256' + remove_attributes = ['height', 'width'] + ignore_duplicate_articles = {'title', 'url'} remove_empty_feeds = True compress_news_images_auto_size = 6 requires_version = (5, 0, 0) - BASE_URL = "https://newrepublic.com" + BASE_URL = 'https://newrepublic.com' - extra_css = """ + extra_css = ''' h1.headline { margin-bottom: 0.4rem; } h2.subheadline { font-style: italic; margin-bottom: 1rem; font-weight: normal; } .article-meta { margin-bottom: 1rem; } @@ -64,15 +64,15 @@ class NewRepublicMagazine(BasicNewsRecipe): } .lede-media .caption, .article-embed .caption { font-size: 0.8rem; } div.author-bios { margin-top: 2rem; font-style: italic; border-top: solid 1px dimgray; } - """ + ''' def _article_endpoint(self, nid): - """ + ''' Graphql endpoint to fetch full article :param nid: :return: - """ - query = """ + ''' + query = ''' query ($id: ID, $nid: ID) { Article(id: $id, nid: $nid) { ...ArticlePageFields @@ -157,12 +157,12 @@ fragment ArticlePageFields on Article { slug label } -}""" - params = {"query": query, "variables": json.dumps({"nid": str(nid)})} - return f"https://newrepublic.com/graphql?{urlencode(params)}" +}''' + params = {'query': query, 'variables': json.dumps({'nid': str(nid)})} + return f'https://newrepublic.com/graphql?{urlencode(params)}' def _resize_image(self, image_url, width, height): - """ + ''' Rewrite the image url to fetch a device appropriate sized one instead of the full-res one @@ -170,76 +170,76 @@ fragment ArticlePageFields on Article { :param width: :param height: :return: - """ + ''' crop_params = { - "auto": "compress", - "ar": f"{width}:{height}", - "fm": "jpg", - "fit": "crop", - "crop": "faces", - "ixlib": "react-9.0.2", - "dpr": 1, - "q": 65, - "w": self.scale_news_images[0] if self.scale_news_images else 800, + 'auto': 'compress', + 'ar': f'{width}:{height}', + 'fm': 'jpg', + 'fit': 'crop', + 'crop': 'faces', + 'ixlib': 'react-9.0.2', + 'dpr': 1, + 'q': 65, + 'w': self.scale_news_images[0] if self.scale_news_images else 800, } url_tuple = urlsplit(image_url) - return f"{url_tuple.scheme}://{url_tuple.netloc}{url_tuple.path}?{urlencode(crop_params)}" + return f'{url_tuple.scheme}://{url_tuple.netloc}{url_tuple.path}?{urlencode(crop_params)}' def populate_article_metadata(self, article, soup, first): # pick up the og link from preprocess_raw_html() and set it as url instead of the api endpoint - og_link = soup.select("[data-og-link]") + og_link = soup.select('[data-og-link]') if og_link: - article.url = og_link[0]["data-og-link"] + article.url = og_link[0]['data-og-link'] def preprocess_raw_html(self, raw_html, url): # formulate the api response into html - article = json.loads(raw_html)["data"]["Article"] + article = json.loads(raw_html)['data']['Article'] # Example: 2022-08-12T10:00:00.000Z - date_published_loc = parse_date(article["publishedAt"]) + date_published_loc = parse_date(article['publishedAt']) # authors - author_bios_html = "" + author_bios_html = '' post_authors = [] try: - post_authors = [a["name"] for a in article.get("authors", [])] + post_authors = [a['name'] for a in article.get('authors', [])] if post_authors: - author_bios_html = "".join( - [a.get("blurb", "") for a in article.get("authors", [])] + author_bios_html = ''.join( + [a.get('blurb', '') for a in article.get('authors', [])] ) author_bios_html = f'
{author_bios_html}
' except (KeyError, TypeError): pass # lede image - lede_image_html = "" - if article.get("ledeImage"): - img = article["ledeImage"] + lede_image_html = '' + if article.get('ledeImage'): + img = article['ledeImage'] lede_img_url = self._resize_image( - urljoin(self.BASE_URL, img["src"]), img["width"], img["height"] + urljoin(self.BASE_URL, img['src']), img['width'], img['height'] ) - lede_image_caption = "" - if article.get("ledeImageRealCaption"): + lede_image_caption = '' + if article.get('ledeImageRealCaption'): lede_image_caption = ( f'{article["ledeImageRealCaption"]}>/span>' ) - lede_image_html = f"""

+ lede_image_html = f'''

{lede_image_caption} -

""" +

''' - body_soup = BeautifulSoup(article["body"], features="html.parser") - for img in body_soup.find_all("img", attrs={"data-serialized": True}): + body_soup = BeautifulSoup(article['body'], features='html.parser') + for img in body_soup.find_all('img', attrs={'data-serialized': True}): try: - img_info = json.loads(img["data-serialized"]) + img_info = json.loads(img['data-serialized']) img_src = self._resize_image( - urljoin(self.BASE_URL, img_info["src"]), - img_info["width"], - img_info["height"], + urljoin(self.BASE_URL, img_info['src']), + img_info['width'], + img_info['height'], ) - img["src"] = img_src - del img["data-serialized"] + img['src'] = img_src + del img['data-serialized'] except: pass - return f""" + return f''' {article["cleanTitle"]}
@@ -255,34 +255,34 @@ fragment ArticlePageFields on Article { {str(body_soup)} {author_bios_html}
- """ + ''' def parse_index(self): br = self.get_browser() - params = "" + params = '' if _issue_url: - month = urlparse(_issue_url).path.split("/")[-1] + month = urlparse(_issue_url).path.split('/')[-1] params = f'?{urlencode({"magazineTag": month})}' - res = br.open_novisit(f"https://newrepublic.com/api/content/magazine{params}") - magazine = json.loads(res.read().decode("utf-8"))["data"] + res = br.open_novisit(f'https://newrepublic.com/api/content/magazine{params}') + magazine = json.loads(res.read().decode('utf-8'))['data'] self.log.debug(f'Found issue: {magazine["metaData"]["issueTag"]["text"]}') self.timefmt = f': {magazine["metaData"]["issueTag"]["text"]}' - self.cover_url = urljoin(self.BASE_URL, magazine["metaData"]["image"]["src"]) + self.cover_url = urljoin(self.BASE_URL, magazine['metaData']['image']['src']) feed_articles = [] for k, articles in magazine.items(): - if not (k.startswith("magazine") and articles): + if not (k.startswith('magazine') and articles): continue try: for article in articles: self.log.debug(f'Found article: {article["title"]}') feed_articles.append( { - "url": self._article_endpoint(article["nid"]), - "title": article["title"].replace("\n", " "), - "description": article.get("deck", ""), - "date": article["publishedAt"], - "section": k[len("magazine") :], + 'url': self._article_endpoint(article['nid']), + 'title': article['title'].replace('\n', ' '), + 'description': article.get('deck', ''), + 'date': article['publishedAt'], + 'section': k[len('magazine') :], } ) except TypeError: @@ -290,24 +290,24 @@ fragment ArticlePageFields on Article { pass sort_sections = [ - "Cover", - "Editorsnote", - "Features", - "StateOfTheNation", - "ResPublica", - "Columns", - "Upfront", - "Backstory", - "SignsAndWonders", - "Usandtheworld", - "Booksandthearts", - "Poetry", - "Exposure", + 'Cover', + 'Editorsnote', + 'Features', + 'StateOfTheNation', + 'ResPublica', + 'Columns', + 'Upfront', + 'Backstory', + 'SignsAndWonders', + 'Usandtheworld', + 'Booksandthearts', + 'Poetry', + 'Exposure', ] sort_category_key = cmp_to_key(lambda a, b: sort_section(a, b, sort_sections)) return [ ( - magazine["metaData"]["issueTag"]["text"], + magazine['metaData']['issueTag']['text'], sorted(feed_articles, key=sort_category_key), ) ] diff --git a/recipes/news24.recipe b/recipes/news24.recipe index 94b9979ba2..a5c4e77d06 100644 --- a/recipes/news24.recipe +++ b/recipes/news24.recipe @@ -3,7 +3,7 @@ from calibre.web.feeds.news import BasicNewsRecipe class AdvancedUserRecipe1375900744(BasicNewsRecipe): title = u'News24' - description = "News24." + description = 'News24.' __author__ = 'Nicki de Wet' publisher = 'Media24' category = 'news, politics, South Africa' @@ -17,10 +17,10 @@ class AdvancedUserRecipe1375900744(BasicNewsRecipe): remove_empty_feeds = True publication_type = 'newsportal' masthead_url = 'http://www.24.com/images/widgethead_news.png' - extra_css = """ + extra_css = ''' body{font-family: Arial,Helvetica,sans-serif } img{display: block} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/news_busters.recipe b/recipes/news_busters.recipe index 7d8ab2d80e..dea3e7ad17 100644 --- a/recipes/news_busters.recipe +++ b/recipes/news_busters.recipe @@ -7,7 +7,7 @@ class NewsBusters(BasicNewsRecipe): __author__ = 'jde' oldest_article = 1 # day max_articles_per_feed = 100 - cover_url = "http://newsbusters.org/sites/all/themes/genesis_nb/images/nb-mrc.png" + cover_url = 'http://newsbusters.org/sites/all/themes/genesis_nb/images/nb-mrc.png' language = 'en' encoding = 'utf8' needs_subscription = False diff --git a/recipes/newsweek_polska.recipe b/recipes/newsweek_polska.recipe index af69641762..ce34da87eb 100644 --- a/recipes/newsweek_polska.recipe +++ b/recipes/newsweek_polska.recipe @@ -71,31 +71,31 @@ class Newsweek(BasicNewsRecipe): strong = p.find('strong') if strong: newest = re.compile( - "Tekst pochodzi z najnowszego numeru Tygodnika Newsweek") + 'Tekst pochodzi z najnowszego numeru Tygodnika Newsweek') if newest.search(str(strong)): strong.extract() continue itunes = p.find('a') if itunes: - reurl = re.compile("itunes.apple.com") + reurl = re.compile('itunes.apple.com') if reurl.search(str(itunes['href'])): p.extract() continue imagedesc = p.find('div', attrs={'class': 'image-desc'}) if imagedesc: - redesc = re.compile("Okładka numeru") + redesc = re.compile('Okładka numeru') if (redesc.search(str(imagedesc))): p.extract() continue # get actual contents for content in article_div.contents: - strs.append("".join(str(content))) + strs.append(''.join(str(content))) # return contents as a string - return u"".join(strs) + return u''.join(strs) # # Articles can be divided into several pages, this method parses them recursevely @@ -108,7 +108,7 @@ class Newsweek(BasicNewsRecipe): matches = re.search(r'
(.*)
', source, re.DOTALL) if matches is None: - print("no article tag found, returning...") + print('no article tag found, returning...') return main_section = BeautifulSoup(matches.group(0)) diff --git a/recipes/nezavisne_novine.recipe b/recipes/nezavisne_novine.recipe index 9ab93d928d..11b8da10af 100644 --- a/recipes/nezavisne_novine.recipe +++ b/recipes/nezavisne_novine.recipe @@ -24,10 +24,10 @@ class NezavisneNovine(BasicNewsRecipe): cover_url = strftime( 'http://pdf.nezavisne.com/slika/novina/nezavisne_novine.jpg?v=%Y%m%d') masthead_url = 'http://www.nezavisne.com/slika/osnova/nezavisne-novine-logo.gif' - extra_css = """ + extra_css = ''' body{font-family: Arial,Helvetica,sans-serif } img{margin-bottom: 0.4em; display:block} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/nikkei_news.recipe b/recipes/nikkei_news.recipe index d193deaf06..28dfdff1c9 100644 --- a/recipes/nikkei_news.recipe +++ b/recipes/nikkei_news.recipe @@ -26,67 +26,67 @@ class NikkeiNet_paper_subscription(BasicNewsRecipe): masthead_url = 'http://cdn.nikkei.co.jp/parts/ds/images/common/st_nikkei_r1_20101003_1.gif' cover_margins = (10, 188, '#ffffff') - remove_tags_before = {'class': "cmn-indent"} + remove_tags_before = {'class': 'cmn-indent'} remove_tags = [ # {'class':"cmn-article_move"}, # {'class':"cmn-pr_list"}, # {'class':"cmnc-zoom"}, - {'class': "cmn-hide"}, + {'class': 'cmn-hide'}, {'name': 'form'}, {'class': 'cmn-print_headline cmn-clearfix'}, {'id': 'ABOUT_NIKKEI'}, ] - remove_tags_after = {'class': "cmn-indent"} + remove_tags_after = {'class': 'cmn-indent'} def get_browser(self): br = BasicNewsRecipe.get_browser(self) if self.username is not None and self.password is not None: - print("-------------------------open top page-------------------------------------") + print('-------------------------open top page-------------------------------------') br.open('http://www.nikkei.com/') - print("-------------------------open first login form-----------------------------") + print('-------------------------open first login form-----------------------------') try: url = list(br.links( - url_regex="www.nikkei.com/etc/accounts/login"))[0].url + url_regex='www.nikkei.com/etc/accounts/login'))[0].url except IndexError: - print("Found IndexError") + print('Found IndexError') url = 'http://www.nikkei.com/etc/accounts/login?dps=3&pageflag=top&url=http%3A%2F%2Fwww.nikkei.com%2F' except StopIteration: url = 'http://www.nikkei.com/etc/accounts/login?dps=3&pageflag=top&url=http%3A%2F%2Fwww.nikkei.com%2F' br.open(url) - print("-------------------------JS redirect(send autoPostForm)--------------------") + print('-------------------------JS redirect(send autoPostForm)--------------------') br.select_form(name='autoPostForm') br.submit() # response = br.response() - print("-------------------------got login form------------------------------------") + print('-------------------------got login form------------------------------------') br.select_form(name='LA7010Form01') br['LA7010Form01:LA7010Email'] = self.username br['LA7010Form01:LA7010Password'] = self.password br.submit(id='LA7010Form01:submitBtn') - print("-------------------------JS redirect---------------------------------------") + print('-------------------------JS redirect---------------------------------------') br.select_form(nr=0) br.submit() return br def cleanup(self): - print("-------------------------logout--------------------------------------------") + print('-------------------------logout--------------------------------------------') self.browser.open('https://regist.nikkei.com/ds/etc/accounts/logout') def parse_index(self): - print("-------------------------get index of paper--------------------------------") + print('-------------------------get index of paper--------------------------------') result = [] soup = self.index_to_soup('http://www.nikkei.com/paper/') - sections = soup.findAll(attrs={'class': re.compile(".*cmn-article_title.*")}) + sections = soup.findAll(attrs={'class': re.compile('.*cmn-article_title.*')}) for sect in sections: - sect_title = sect.find(attrs={'class' : re.compile(".*cmnc-((large)|(middle)|(small)).*")}) + sect_title = sect.find(attrs={'class' : re.compile('.*cmnc-((large)|(middle)|(small)).*')}) if sect_title is None: continue sect_title = sect_title.contents[0] sect_result = [] url = sect.a['href'] - url = re.sub("/article/", "/print-article/", url) + url = re.sub('/article/', '/print-article/', url) url = 'http://www.nikkei.com' + url sect_result.append(dict(title=sect_title, url=url, date='',description='', content='')) result.append([sect_title, sect_result]) @@ -95,11 +95,11 @@ class NikkeiNet_paper_subscription(BasicNewsRecipe): def populate_article_metadata(self, article, soup, first): try: elms = soup.findAll( - 'div', {"class": "cmn-article_text JSID_key_fonttxt"}) + 'div', {'class': 'cmn-article_text JSID_key_fonttxt'}) elm_text = u'◆'.join( [self.tag_to_string(elm).strip() for elm in elms]) elm_text = unicodedata.normalize('NFKC', elm_text) article.summary = article.text_summary = elm_text except: - self.log("Error: Failed to get article summary.") + self.log('Error: Failed to get article summary.') return diff --git a/recipes/nikkeiasia.recipe b/recipes/nikkeiasia.recipe index c9c55c88b4..5a725bb808 100644 --- a/recipes/nikkeiasia.recipe +++ b/recipes/nikkeiasia.recipe @@ -29,12 +29,12 @@ class Nikkei(BasicNewsRecipe): encoding = 'utf-8' use_embedded_content = False - extra_css = """ + extra_css = ''' .subhead { font-style:italic; color:#202020; } em, blockquote { color:#202020; } .sec, .byline { font-size:small; font-weight:bold; } .article__image, .article__caption { font-size:small; text-align:center; } - """ + ''' recipe_specific_options = { 'date': {'short': 'The edition date (YYYY-MM-DD format)', 'long': '2024-09-19'} diff --git a/recipes/njuz_net.recipe b/recipes/njuz_net.recipe index 7db58348d5..bd9ae24395 100644 --- a/recipes/njuz_net.recipe +++ b/recipes/njuz_net.recipe @@ -23,12 +23,12 @@ class NjuzNet(BasicNewsRecipe): language = 'sr' publication_type = 'newsportal' masthead_url = 'http://www.njuz.net/njuznet.jpg' - extra_css = """ + extra_css = ''' @font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .articledescription{font-family: serif1, serif} .wp-caption-text{font-size: x-small} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/novilist_novine_hr.recipe b/recipes/novilist_novine_hr.recipe index 6939447607..eeb3c1e6f6 100644 --- a/recipes/novilist_novine_hr.recipe +++ b/recipes/novilist_novine_hr.recipe @@ -27,14 +27,14 @@ class NoviList_hr(BasicNewsRecipe): needs_subscription = True masthead_url = 'http://novine.novilist.hr/images/system/novilist-logo.jpg' index = 'http://novine.novilist.hr/' - extra_css = """ + extra_css = ''' @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: Geneva,Arial,Helvetica,Swiss,sans1,sans-serif } img{display:block; margin-bottom: 0.4em; margin-top: 0.4em} .nadnaslov,.podnaslov{font-size: small; display: block; margin-bottom: 1em} .naslov{font-size: x-large; color: maroon; font-weight: bold; display: block; margin-bottom: 1em;} p{display: block} - """ + ''' preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] diff --git a/recipes/novosti.recipe b/recipes/novosti.recipe index e889a3fc46..2c926ab51b 100644 --- a/recipes/novosti.recipe +++ b/recipes/novosti.recipe @@ -24,12 +24,12 @@ class Novosti(BasicNewsRecipe): language = 'sr' publication_type = 'newspaper' masthead_url = 'http://www.novosti.rs/images/basic/logo-print.png' - extra_css = """ @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} + extra_css = ''' @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} .article_description,body{font-family: Arial,Helvetica,sans1,sans-serif} .author{font-size: small} .articleLead{font-size: large; font-weight: bold} img{display: block; margin-bottom: 1em; margin-top: 1em} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language, 'pretty_print': True diff --git a/recipes/nrc.nl.recipe b/recipes/nrc.nl.recipe index a03fb6269e..4f5b29fffc 100644 --- a/recipes/nrc.nl.recipe +++ b/recipes/nrc.nl.recipe @@ -39,7 +39,7 @@ class NRC(BasicNewsRecipe): ), dict(name=['script', 'noscript', 'style']), ] - remove_attributes = ["class", "id", "name", "style"] + remove_attributes = ['class', 'id', 'name', 'style'] encoding = 'utf-8' no_stylesheets = True ignore_duplicate_articles = {'url'} @@ -52,8 +52,8 @@ class NRC(BasicNewsRecipe): title_regexp = None @staticmethod - def _monthly_list_url(date, fmt="%Y/%m/"): - return "https://www.nrc.nl/de/data/NH/" + date.strftime(fmt) + def _monthly_list_url(date, fmt='%Y/%m/'): + return 'https://www.nrc.nl/de/data/NH/' + date.strftime(fmt) def _clean_article_title(self, title): if not title: @@ -62,7 +62,7 @@ class NRC(BasicNewsRecipe): self.title_regexp = re.compile( r'([^<]+)\s*' ) - return self.title_regexp.sub(r"\1 ", title) + return self.title_regexp.sub(r'\1 ', title) def parse_index(self): sections = [] @@ -88,43 +88,43 @@ class NRC(BasicNewsRecipe): issues = json.loads(r.read()) if len(issues) > 0: issue_date = datetime.datetime.strptime( - issues[0]["published_at"], "%Y-%m-%dT%H:%M:%SZ" + issues[0]['published_at'], '%Y-%m-%dT%H:%M:%SZ' ) - issue_url = self._monthly_list_url(issue_date, "%Y/%m/%d/") - self.frontpage = issues[0]["frontpage"] + issue_url = self._monthly_list_url(issue_date, '%Y/%m/%d/') + self.frontpage = issues[0]['frontpage'] break if issue_url is None: return [] with closing(self.browser.open(Request(issue_url, None, headers))) as r: edition = json.loads(r.read()) documents = {} - for headline in edition["paperheadlines"]: - item = headline["item"] - documents[headline["document_id"]] = dict( - url=item["full_url"], - headline=self._clean_article_title(item["headline"]) + for headline in edition['paperheadlines']: + item = headline['item'] + documents[headline['document_id']] = dict( + url=item['full_url'], + headline=self._clean_article_title(item['headline']) ) - for section in edition["sections"]: + for section in edition['sections']: articles = [] - for doc in section["document_ids"]: + for doc in section['document_ids']: if doc not in documents: self.log.warn('Document not found:', doc) continue articles.append( dict( - title=documents[doc]["headline"], url=documents[doc]["url"] + title=documents[doc]['headline'], url=documents[doc]['url'] ) ) - sections.append((section["name"], articles)) + sections.append((section['name'], articles)) return sections def preprocess_html(self, soup): for tag in soup(): if tag.name == 'img': if tag.has_attr('data-src-medium'): - tag['src'] = tag['data-src-medium'].split("|")[0] + tag['src'] = tag['data-src-medium'].split('|')[0] elif tag.has_attr('data-src'): - tag['src'] = tag['data-src'].split("|")[0] + tag['src'] = tag['data-src'].split('|')[0] if tag['src'].startswith('//'): tag['src'] = 'https:' + tag['src'] elif tag['src'].startswith('/'): diff --git a/recipes/nrc_next.recipe b/recipes/nrc_next.recipe index 42886190d2..bc6e73e5d3 100644 --- a/recipes/nrc_next.recipe +++ b/recipes/nrc_next.recipe @@ -62,7 +62,7 @@ class NRCNext(BasicNewsRecipe): zfile = zipfile.ZipFile(BytesIO(epubraw), 'r') zfile.extractall(self.output_dir) namelist = zfile.namelist() - emre = re.compile("<em(?:.*)>(.*)</em>") + emre = re.compile('<em(?:.*)>(.*)</em>') subst = '\\1' for name in namelist: _, ext = os.path.splitext(name) diff --git a/recipes/nspm.recipe b/recipes/nspm.recipe index 2b89b3a604..f533f4e5fc 100644 --- a/recipes/nspm.recipe +++ b/recipes/nspm.recipe @@ -33,13 +33,13 @@ class Nspm(BasicNewsRecipe): remove_empty_feeds = True publication_type = 'magazine' masthead_url = 'http://www.nspm.rs/templates/jsn_epic_pro/images/logol.jpg' - extra_css = """ @font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} + extra_css = ''' @font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: "Times New Roman", serif1, serif} .article_description{font-family: Arial, sans1, sans-serif} img{margin-top:0.5em; margin-bottom: 0.7em; display: block} .author{color: #990000; font-weight: bold} - .author,.createdate{font-size: 0.9em} """ + .author,.createdate{font-size: 0.9em} ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language, 'pretty_print': True diff --git a/recipes/nspm_int.recipe b/recipes/nspm_int.recipe index a562a05628..9309c88bfb 100644 --- a/recipes/nspm_int.recipe +++ b/recipes/nspm_int.recipe @@ -22,12 +22,12 @@ class Nspm_int(BasicNewsRecipe): delay = 2 publication_type = 'magazine' masthead_url = 'http://www.nspm.rs/templates/jsn_epic_pro/images/logol.jpg' - extra_css = """ + extra_css = ''' body{font-family: "Times New Roman", serif} .article_description{font-family: Arial, sans-serif} img{margin-top:0.5em; margin-bottom: 0.7em} .author{color: #990000; font-weight: bold} - .author,.createdate{font-size: 0.9em} """ + .author,.createdate{font-size: 0.9em} ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language, 'linearize_tables': True diff --git a/recipes/nyt_magazine.recipe b/recipes/nyt_magazine.recipe index 52c4a38b0d..e548976fcc 100644 --- a/recipes/nyt_magazine.recipe +++ b/recipes/nyt_magazine.recipe @@ -66,7 +66,7 @@ class NytMag(BasicNewsRecipe): if c.lower() == 'yes': self.compress_news_images = True - extra_css = """ + extra_css = ''' .byl, .time { font-size:small; color:#202020; } .cap { font-size:small; text-align:center; } .cred { font-style:italic; font-size:small; } @@ -74,7 +74,7 @@ class NytMag(BasicNewsRecipe): .sc { font-variant: small-caps; } .lbl { font-size:small; color:#404040; } img { display:block; margin:0 auto; } - """ + ''' @property def nyt_parser(self): diff --git a/recipes/nyt_tmag.recipe b/recipes/nyt_tmag.recipe index b4b0a25a18..718a37adcb 100644 --- a/recipes/nyt_tmag.recipe +++ b/recipes/nyt_tmag.recipe @@ -66,7 +66,7 @@ class NytMag(BasicNewsRecipe): if c.lower() == 'yes': self.compress_news_images = True - extra_css = """ + extra_css = ''' .byl, .time { font-size:small; color:#202020; } .cap { font-size:small; text-align:center; } .cred { font-style:italic; font-size:small; } @@ -74,7 +74,7 @@ class NytMag(BasicNewsRecipe): .sc { font-variant: small-caps; } .lbl { font-size:small; color:#404040; } img { display:block; margin:0 auto; } - """ + ''' @property def nyt_parser(self): diff --git a/recipes/nytfeeds.recipe b/recipes/nytfeeds.recipe index bf05758c1e..136e707ec1 100644 --- a/recipes/nytfeeds.recipe +++ b/recipes/nytfeeds.recipe @@ -92,7 +92,7 @@ class NytFeeds(BasicNewsRecipe): if c.lower() == 'yes': self.compress_news_images = True - extra_css = """ + extra_css = ''' .byl, .time { font-size:small; color:#202020; } .cap { font-size:small; text-align:center; } .cred { font-style:italic; font-size:small; } @@ -100,7 +100,7 @@ class NytFeeds(BasicNewsRecipe): .sc { font-variant: small-caps; } .lbl { font-size:small; color:#404040; } img { display:block; margin:0 auto; } - """ + ''' @property def nyt_parser(self): diff --git a/recipes/nytimes.recipe b/recipes/nytimes.recipe index fb42463da6..7d81bb0d15 100644 --- a/recipes/nytimes.recipe +++ b/recipes/nytimes.recipe @@ -87,7 +87,7 @@ class NewYorkTimes(BasicNewsRecipe): is_web_edition = True oldest_web_edition_article = 7 # days - extra_css = """ + extra_css = ''' .byl, .time { font-size:small; color:#202020; } .cap { font-size:small; text-align:center; } .cred { font-style:italic; font-size:small; } @@ -95,7 +95,7 @@ class NewYorkTimes(BasicNewsRecipe): .sc { font-variant: small-caps; } .lbl { font-size:small; color:#404040; } img { display:block; margin:0 auto; } - """ + ''' @property def nyt_parser(self): diff --git a/recipes/nytimes_sports.recipe b/recipes/nytimes_sports.recipe index 8086ca729a..2e752483c6 100644 --- a/recipes/nytimes_sports.recipe +++ b/recipes/nytimes_sports.recipe @@ -6,7 +6,7 @@ from __future__ import with_statement __license__ = 'GPL 3' __copyright__ = 'zotzo' __docformat__ = 'restructuredtext en' -""" +''' http://fifthdown.blogs.nytimes.com/ http://offthedribble.blogs.nytimes.com/ http://thequad.blogs.nytimes.com/ @@ -16,7 +16,7 @@ http://bats.blogs.nytimes.com/ http://straightsets.blogs.nytimes.com/ http://formulaone.blogs.nytimes.com/ http://onpar.blogs.nytimes.com/ -""" +''' from calibre.web.feeds.news import BasicNewsRecipe diff --git a/recipes/nytimes_sub.recipe b/recipes/nytimes_sub.recipe index d069d1545d..ef0dd2157d 100644 --- a/recipes/nytimes_sub.recipe +++ b/recipes/nytimes_sub.recipe @@ -87,7 +87,7 @@ class NewYorkTimes(BasicNewsRecipe): is_web_edition = False oldest_web_edition_article = 7 # days - extra_css = """ + extra_css = ''' .byl, .time { font-size:small; color:#202020; } .cap { font-size:small; text-align:center; } .cred { font-style:italic; font-size:small; } @@ -95,7 +95,7 @@ class NewYorkTimes(BasicNewsRecipe): .sc { font-variant: small-caps; } .lbl { font-size:small; color:#404040; } img { display:block; margin:0 auto; } - """ + ''' @property def nyt_parser(self): diff --git a/recipes/nytimes_tech.recipe b/recipes/nytimes_tech.recipe index a7d9009f06..5d76fcd862 100644 --- a/recipes/nytimes_tech.recipe +++ b/recipes/nytimes_tech.recipe @@ -60,7 +60,7 @@ class NytTech(BasicNewsRecipe): if c.lower() == 'yes': self.compress_news_images = True - extra_css = """ + extra_css = ''' .byl, .time { font-size:small; color:#202020; } .cap { font-size:small; text-align:center; } .cred { font-style:italic; font-size:small; } @@ -68,7 +68,7 @@ class NytTech(BasicNewsRecipe): .sc { font-variant: small-caps; } .lbl { font-size:small; color:#404040; } img { display:block; margin:0 auto; } - """ + ''' @property def nyt_parser(self): diff --git a/recipes/nytimesbook.recipe b/recipes/nytimesbook.recipe index fb9e3558c2..df8eddc39a 100644 --- a/recipes/nytimesbook.recipe +++ b/recipes/nytimesbook.recipe @@ -24,7 +24,7 @@ class NewYorkTimesBookReview(BasicNewsRecipe): ignore_duplicate_articles = {'title', 'url'} encoding = 'utf-8' - extra_css = """ + extra_css = ''' .byl, .time { font-size:small; color:#202020; } .cap { font-size:small; text-align:center; } .cred { font-style:italic; font-size:small; } @@ -32,7 +32,7 @@ class NewYorkTimesBookReview(BasicNewsRecipe): .sc { font-variant: small-caps; } .lbl { font-size:small; color:#404040; } img { display:block; margin:0 auto; } - """ + ''' articles_are_obfuscated = use_wayback_machine diff --git a/recipes/observatorul_cultural.recipe b/recipes/observatorul_cultural.recipe index 4c91f8859e..304024a53c 100644 --- a/recipes/observatorul_cultural.recipe +++ b/recipes/observatorul_cultural.recipe @@ -32,7 +32,7 @@ class ObservatorulCultural(BasicNewsRecipe): soup = self.index_to_soup( 'http://www.observatorcultural.ro/Arhiva*-archive.html') issueTag = soup.find('a', href=re.compile( - "observatorcultural.ro\\/Numarul")) + 'observatorcultural.ro\\/Numarul')) issueURL = issueTag['href'] print(issueURL) issueSoup = self.index_to_soup(issueURL) diff --git a/recipes/observer_gb.recipe b/recipes/observer_gb.recipe index dc49dc2d23..7a844061de 100644 --- a/recipes/observer_gb.recipe +++ b/recipes/observer_gb.recipe @@ -27,18 +27,18 @@ class Guardian(BasicNewsRecipe): keep_only_tags = [ dict(name='div', attrs={ - 'id': ["content", "article_header", "main-article-info", ]}), + 'id': ['content', 'article_header', 'main-article-info', ]}), ] remove_tags = [ dict(name='div', attrs={ - 'class': ["video-content", "videos-third-column"]}), + 'class': ['video-content', 'videos-third-column']}), dict(name='div', attrs={ - 'id': ["article-toolbox", "subscribe-feeds", ]}), + 'id': ['article-toolbox', 'subscribe-feeds', ]}), dict(name='div', attrs={ - 'class': ["promo-component bookshop-books-promo bookshop-books"]}), - dict(name='ul', attrs={'class': ["pagination"]}), - dict(name='ul', attrs={'id': ["content-actions"]}), - dict(name='li', attrs={'id': ["product-image"]}), + 'class': ['promo-component bookshop-books-promo bookshop-books']}), + dict(name='ul', attrs={'class': ['pagination']}), + dict(name='ul', attrs={'id': ['content-actions']}), + dict(name='li', attrs={'id': ['product-image']}), ] use_embedded_content = False diff --git a/recipes/oc_register.recipe b/recipes/oc_register.recipe index 5e489b404d..ec3179bab9 100644 --- a/recipes/oc_register.recipe +++ b/recipes/oc_register.recipe @@ -36,18 +36,18 @@ class OrangeCountyRegister(BasicNewsRecipe): def parsePage(self, index): if self.debugMessages is True: - print("\n\nStarting " + self.feeds[index][0]) + print('\n\nStarting ' + self.feeds[index][0]) articleList = [] soup = self.index_to_soup(self.feeds[index][1]) # Have this index page now. # look for a.article-title # If any, the description is
- for newsentry in soup.findAll("a", {"class": "article-title"}): + for newsentry in soup.findAll('a', {'class': 'article-title'}): print('Next up:') print(newsentry) - title = newsentry["title"] + title = newsentry['title'] url = newsentry['href'] - print("Title: ") + print('Title: ') print(title) print('URL') print(url) @@ -66,19 +66,19 @@ class OrangeCountyRegister(BasicNewsRecipe): def extract_readable_article(self, html, url): cleanedHTML = super(OrangeCountyRegister, self).extract_readable_article(html, url) - print("Processing html for author") + print('Processing html for author') # Find the attribs... attribDict = self.htmlToAttribsDict(html) - print("dict is type...") + print('dict is type...') print(type(attribDict)) author = attribDict.get('Byline') if author is not None: # add author code after - print("Adding author in meta") + print('Adding author in meta') print(author) cleanedHTML = cleanedHTML.replace( - "", - "\n
\n" + '', + '\n
\n' ) else: print('no author found') @@ -92,7 +92,7 @@ class OrangeCountyRegister(BasicNewsRecipe): def htmlToAttribsDict(self, rawHTML): tokenStart = 'dataLayer.push({' tokenEnd = '});' - print("1") + print('1') startJSON = rawHTML.find(tokenStart) if (startJSON < 0): return @@ -101,13 +101,13 @@ class OrangeCountyRegister(BasicNewsRecipe): if (endJSON < 0): return JSON = JSONBeginning[:endJSON + 1] - JSONQuoted = JSON.replace("'", "\"") + JSONQuoted = JSON.replace("'", '"') try: metadata = json.loads(JSONQuoted) pprint(metadata) return metadata except ValueError: - print("Could not decode JSON:") + print('Could not decode JSON:') print(JSONQuoted) return None diff --git a/recipes/omgubuntu.recipe b/recipes/omgubuntu.recipe index 9242c5c683..0da184ad37 100644 --- a/recipes/omgubuntu.recipe +++ b/recipes/omgubuntu.recipe @@ -5,8 +5,8 @@ from calibre.web.feeds.news import BasicNewsRecipe class OMGUbuntu(BasicNewsRecipe): - title = u"Omg! Ubuntu!" - description = u"Online news site covering Ubuntu activities. Recipe pulls articles from past 7 days." + title = u'Omg! Ubuntu!' + description = u'Online news site covering Ubuntu activities. Recipe pulls articles from past 7 days.' language = 'en' oldest_article = 7 max_articles_per_feed = 100 diff --git a/recipes/orient_21.recipe b/recipes/orient_21.recipe index 6b06434e79..94ac39e363 100644 --- a/recipes/orient_21.recipe +++ b/recipes/orient_21.recipe @@ -32,9 +32,9 @@ class OrientXXIRecipe(BasicNewsRecipe): ''' def default_cover(self, cover_file): - """ + ''' Crée une couverture personnalisée avec le logo - """ + ''' from qt.core import QColor, QFont, QImage, QPainter, QPen, QRect, Qt from calibre.gui2 import ensure_app, load_builtin_fonts, pixmap_to_data @@ -50,7 +50,7 @@ class OrientXXIRecipe(BasicNewsRecipe): weekday = french_weekday[wkd] month = french_month[today.month] - date_str = f"{weekday} {today.day} {month} {today.year}" + date_str = f'{weekday} {today.day} {month} {today.year}' edition = today.strftime('Édition de %Hh') # Image de base diff --git a/recipes/ottawa_citizen.recipe b/recipes/ottawa_citizen.recipe index 7b0b152767..0154d55758 100644 --- a/recipes/ottawa_citizen.recipe +++ b/recipes/ottawa_citizen.recipe @@ -164,24 +164,24 @@ class CanWestPaper(BasicNewsRecipe): continue break if daysback == 7: - self.log("\nCover unavailable") + self.log('\nCover unavailable') cover = None return cover def fixChars(self, string): # Replace lsquo (\x91) - fixed = re.sub("\x91", "‘", string) + fixed = re.sub('\x91', '‘', string) # Replace rsquo (\x92) - fixed = re.sub("\x92", "’", fixed) + fixed = re.sub('\x92', '’', fixed) # Replace ldquo (\x93) - fixed = re.sub("\x93", "“", fixed) + fixed = re.sub('\x93', '“', fixed) # Replace rdquo (\x94) - fixed = re.sub("\x94", "”", fixed) + fixed = re.sub('\x94', '”', fixed) # Replace ndash (\x96) - fixed = re.sub("\x96", "–", fixed) + fixed = re.sub('\x96', '–', fixed) # Replace mdash (\x97) - fixed = re.sub("\x97", "—", fixed) - fixed = re.sub("’", "’", fixed) + fixed = re.sub('\x97', '—', fixed) + fixed = re.sub('’', '’', fixed) return fixed def massageNCXText(self, description): @@ -262,10 +262,10 @@ class CanWestPaper(BasicNewsRecipe): if url.startswith('/'): url = self.url_prefix + url if not url.startswith(self.url_prefix): - print("Rejected " + url) + print('Rejected ' + url) return if url in self.url_list: - print("Rejected dup " + url) + print('Rejected dup ' + url) return self.url_list.append(url) title = self.tag_to_string(atag, False) @@ -277,8 +277,8 @@ class CanWestPaper(BasicNewsRecipe): return dtag = adiv.find('div', 'content') description = '' - print("URL " + url) - print("TITLE " + title) + print('URL ' + url) + print('TITLE ' + title) if dtag is not None: stag = dtag.span if stag is not None: @@ -286,18 +286,18 @@ class CanWestPaper(BasicNewsRecipe): description = self.tag_to_string(stag, False) else: description = self.tag_to_string(dtag, False) - print("DESCRIPTION: " + description) + print('DESCRIPTION: ' + description) if key not in articles: articles[key] = [] articles[key].append(dict( title=title, url=url, date='', description=description, author='', content='')) def parse_web_index(key, keyurl): - print("Section: " + key + ': ' + self.url_prefix + keyurl) + print('Section: ' + key + ': ' + self.url_prefix + keyurl) try: soup = self.index_to_soup(self.url_prefix + keyurl) except: - print("Section: " + key + ' NOT FOUND') + print('Section: ' + key + ' NOT FOUND') return ans.append(key) mainsoup = soup.find('div', 'bodywrapper') diff --git a/recipes/outlook_india.recipe b/recipes/outlook_india.recipe index 71f40a34c8..0f7128ee92 100644 --- a/recipes/outlook_india.recipe +++ b/recipes/outlook_india.recipe @@ -8,7 +8,7 @@ class outlook(BasicNewsRecipe): __author__ = 'unkn0wn' description = ( 'Outlook covers the latest India news, analysis, business news and long-form stories on culture,' - ' money market and personal finance. Read India\'s best online magazine.' + " money market and personal finance. Read India's best online magazine." ) language = 'en_IN' use_embedded_content = False diff --git a/recipes/pagina12.recipe b/recipes/pagina12.recipe index 54ab8ae358..0bff247ba1 100644 --- a/recipes/pagina12.recipe +++ b/recipes/pagina12.recipe @@ -33,7 +33,7 @@ class Pagina12(BasicNewsRecipe): articles_are_obfuscated = True temp_files = [] fetch_retries = 10 - extra_css = """ + extra_css = ''' body{font-family: "Open Sans", sans-serif} .article-date{font-size: small; margin-bottom: 1em;} .article-title{font-size: x-large; font-weight: bold; display: block; margin-bottom: 1em; margin-top: 1em;} @@ -43,7 +43,7 @@ class Pagina12(BasicNewsRecipe): img{margin-top:1em; margin-bottom: 1em; display:block} .article-text p:first-letter{display: inline; font-size: xx-large; font-weight: bold} .article-prefix{font-family: "Archivo Narrow",Helvetica,sans-serif; font-size: small; text-transform: uppercase;} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language @@ -112,6 +112,6 @@ class Pagina12(BasicNewsRecipe): self.temp_files.append(tfile) result = tfile.name except: - self.info("Retrying download...") + self.info('Retrying download...') count += 1 return result diff --git a/recipes/parool.recipe b/recipes/parool.recipe index fd674d1522..9fea4d9d8d 100644 --- a/recipes/parool.recipe +++ b/recipes/parool.recipe @@ -26,7 +26,7 @@ class Parool(BasicNewsRecipe): dict(attrs={'data-element-id': ['article-element-authors']}), dict(name=['script', 'noscript', 'style']), ] - remove_attributes = ["class", "id", "name", "style"] + remove_attributes = ['class', 'id', 'name', 'style'] encoding = 'utf-8' no_stylesheets = True ignore_duplicate_articles = {'url'} @@ -50,7 +50,7 @@ class Parool(BasicNewsRecipe): teaser_label = self.tag_to_string(header.find('h4').find('span', attrs={'class': 'teaser__label'})).strip() teaser_sublabel = self.tag_to_string(header.find('h4').find('span', attrs={'class': 'teaser__sublabel'})).strip() teaser_title = self.tag_to_string(header.find('h3').find('span', attrs={'class': 'teaser__title__value--short'})).strip() - ignore = { "dirkjan", "s1ngle", "pukkels", "hein de kort" } + ignore = { 'dirkjan', 's1ngle', 'pukkels', 'hein de kort' } if teaser_label.lower() in ignore: continue parts = [] @@ -74,13 +74,13 @@ class Parool(BasicNewsRecipe): if tag['src'][0] == '/': tag['src'] = 'https://www.parool.nl' + tag['src'] for tag in soup(): - if tag.name == "picture": - tag.replaceWith(tag.find("img")) + if tag.name == 'picture': + tag.replaceWith(tag.find('img')) comic_articles = { - "Alle strips van Dirkjan", - "S1NGLE", - "Pukkels", - "Bekijk hier alle cartoons van Hein de Kort", + 'Alle strips van Dirkjan', + 'S1NGLE', + 'Pukkels', + 'Bekijk hier alle cartoons van Hein de Kort', } if self.tag_to_string(soup.find('h1')).strip() in comic_articles: for node in soup.find('figure').find_next_siblings(): @@ -93,8 +93,8 @@ class Parool(BasicNewsRecipe): 'Accept': 'application/json, text/javascript, */*; q=0.01', 'DNT': '1', } - url = "https://login-api.e-pages.dk/v1/krant.parool.nl/folders" + url = 'https://login-api.e-pages.dk/v1/krant.parool.nl/folders' with closing(self.browser.open(Request(url, None, headers))) as r: folders = json.loads(r.read()) - return folders["objects"][0]["teaser_medium"] + return folders['objects'][0]['teaser_medium'] return None diff --git a/recipes/pecat.recipe b/recipes/pecat.recipe index 6426d36221..4d70704a99 100644 --- a/recipes/pecat.recipe +++ b/recipes/pecat.recipe @@ -25,11 +25,11 @@ class Pecat_rs(BasicNewsRecipe): ignore_duplicate_articles = {'url'} needs_subscription = 'optional' publication_type = 'magazine' - extra_css = """ + extra_css = ''' body{font-family: Arial,Helvetica,sans1,sans-serif} img{display: block; margin-bottom: 1em; margin-top: 1em} p{display: block; margin-bottom: 1em; margin-top: 1em} - """ + ''' conversion_options = { 'comment': description, 'tags': 'politika, Srbija', 'publisher': 'Pecat', 'language': language diff --git a/recipes/people_daily.recipe b/recipes/people_daily.recipe index 4ad18a436c..e20d070a83 100644 --- a/recipes/people_daily.recipe +++ b/recipes/people_daily.recipe @@ -89,11 +89,11 @@ class AdvancedUserRecipe1277129332(BasicNewsRecipe): # dict(name='p'), # ] remove_tags = [ - dict(name='div', class_="channel cf") + dict(name='div', class_='channel cf') ] - remove_tags_before = [dict(name='div', class_="layout rm_txt cf")] - remove_tags_after = [dict(name='div', class_="edit cf")] + remove_tags_before = [dict(name='div', class_='layout rm_txt cf')] + remove_tags_after = [dict(name='div', class_='edit cf')] def append_page(self, soup, appendtag, position): pager = soup.find('img', attrs={'src': '/img/next_b.gif'}) @@ -135,6 +135,6 @@ class AdvancedUserRecipe1277129332(BasicNewsRecipe): try: br.open(cover) except: - self.log("\nCover unavailable: " + cover) + self.log('\nCover unavailable: ' + cover) cover = None return cover diff --git a/recipes/pescanik.recipe b/recipes/pescanik.recipe index 365d74724e..157bb4b7e2 100644 --- a/recipes/pescanik.recipe +++ b/recipes/pescanik.recipe @@ -24,11 +24,11 @@ class Pescanik(BasicNewsRecipe): language = 'sr' publication_type = 'newsportal' masthead_url = 'http://pescanik.net/wp-content/uploads/2011/10/logo1.png' - extra_css = """ + extra_css = ''' @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: Verdana,Arial,Tahoma,sans1,sans-serif} #BlogTitle{font-size: xx-large; font-weight: bold} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/politiko_dk.recipe b/recipes/politiko_dk.recipe index 5d5228c6c3..82a602eb8e 100644 --- a/recipes/politiko_dk.recipe +++ b/recipes/politiko_dk.recipe @@ -26,10 +26,10 @@ class PolitikoDK(BasicNewsRecipe): auto_cleanup = False keep_only_tags = [ - dict(name="h1", attrs={'class': 'article-headline'}), - dict(name="p", attrs={'class': 'article-summary'}), - dict(name="div", attrs={'class': 'article-date'}), - dict(name="div", attrs={'class': 'article-content'}), + dict(name='h1', attrs={'class': 'article-headline'}), + dict(name='p', attrs={'class': 'article-summary'}), + dict(name='div', attrs={'class': 'article-date'}), + dict(name='div', attrs={'class': 'article-content'}), ] # Feed are found here: http://www.b.dk/rss diff --git a/recipes/portafolio.recipe b/recipes/portafolio.recipe index ffdc530f85..df17cfb4dc 100644 --- a/recipes/portafolio.recipe +++ b/recipes/portafolio.recipe @@ -20,12 +20,12 @@ class AdvancedUserRecipe1311799898(BasicNewsRecipe): masthead_url = 'http://www.portafolio.co/sites/portafolio.co/themes/portafolio_2011/logo.png' publication_type = 'newspaper' - extra_css = """ + extra_css = ''' p{text-align: justify; font-size: 100%} body{ text-align: left; font-size:100% } h1{font-family: sans-serif; font-size:150%; font-weight:bold; text-align: justify; } h3{font-family: sans-serif; font-size:100%; font-style: italic; text-align: justify; } - """ + ''' feeds = [(u'Negocios', u'http://www.portafolio.co/negocios/feed'), (u'Economia', u'http://www.portafolio.co/economia/feed'), diff --git a/recipes/pravda_por.recipe b/recipes/pravda_por.recipe index 274576b4ee..31b88dec59 100644 --- a/recipes/pravda_por.recipe +++ b/recipes/pravda_por.recipe @@ -22,10 +22,10 @@ class Pravda_port(BasicNewsRecipe): remove_empty_feeds = True publication_type = 'newspaper' masthead_url = 'http://port.pravda.ru/pix/logo.gif' - extra_css = """ + extra_css = ''' body{font-family: Arial,sans-serif } img{margin-bottom: 0.4em; display:block} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/presse_portal.recipe b/recipes/presse_portal.recipe index eda9430236..e998e33e7c 100644 --- a/recipes/presse_portal.recipe +++ b/recipes/presse_portal.recipe @@ -24,7 +24,7 @@ class PressePortalDE(BasicNewsRecipe): # add date to description so for dayly downloads you can find them easier # ---- can be edit by user description = description + ' fetched: ' + \ - datetime.now().strftime("%Y-%m-%d") # %H:%M:%S") + datetime.now().strftime('%Y-%m-%d') # %H:%M:%S") # Who published the content? publisher = u'Presseportal.de' # What is the content of? diff --git a/recipes/private_eye.recipe b/recipes/private_eye.recipe index 5881eb5b90..4f0076e06b 100644 --- a/recipes/private_eye.recipe +++ b/recipes/private_eye.recipe @@ -77,10 +77,10 @@ class PrivateEyeRecipe(BasicNewsRecipe): try: day, month, year = next_issue_text.split(' ') day = ''.join(c for c in day if c.isdigit()) - pub_date = datetime.strptime(" ".join((day, month, year)), "%d %B %Y") - timedelta(12) + pub_date = datetime.strptime(' '.join((day, month, year)), '%d %B %Y') - timedelta(12) self.log('pub-date:', pub_date) - self.conversion_options.update({'pubdate': datetime.strftime(pub_date, "%d %B %Y").lstrip("0")}) - title = self.title + " " + datetime.strftime(pub_date, "%Y-%m-%d") + self.conversion_options.update({'pubdate': datetime.strftime(pub_date, '%d %B %Y').lstrip('0')}) + title = self.title + ' ' + datetime.strftime(pub_date, '%Y-%m-%d') self.conversion_options.update({'title': title}) self.conversion_options.update({'title_sort': title}) except (TypeError, ValueError): @@ -124,13 +124,13 @@ class PrivateEyeRecipe(BasicNewsRecipe): def preprocess_html(self, soup): # Remove tag link to crossword image for tag in soup.findAll('a', {'href': re.compile(r'/pictures/crossword/')}): - self.log("Removing link to crossword image...") + self.log('Removing link to crossword image...') tag.unwrap() # Remove align tag in crossword image (so float right works) for tag in soup.findAll('img', {'src': re.compile(r'/pictures/crossword/')}): - if "align" in tag.attrs: - self.log("Removing crossword image align attribute...") + if 'align' in tag.attrs: + self.log('Removing crossword image align attribute...') del tag.attrs['align'] return soup @@ -138,10 +138,10 @@ class PrivateEyeRecipe(BasicNewsRecipe): # We remove vast swathes of HTML which is not part of the articles. # Remove sibling content remove_tags_before = [ - {'name': 'div', 'class': "article"}, - {'name': 'div', 'id': "page"}, - {'name': 'div', 'id': "page-wide"}, - {'name': 'div', 'id': "content"}, + {'name': 'div', 'class': 'article'}, + {'name': 'div', 'id': 'page'}, + {'name': 'div', 'id': 'page-wide'}, + {'name': 'div', 'id': 'content'}, {'name': 'a', ' attrs': {'href': 'https://shop.private-eye.co.uk'}}, ] remove_tags_after = remove_tags_before.copy() diff --git a/recipes/pro_physik.recipe b/recipes/pro_physik.recipe index 8c945069fb..869f5f0462 100644 --- a/recipes/pro_physik.recipe +++ b/recipes/pro_physik.recipe @@ -45,7 +45,7 @@ class AdvancedUserRecipe1303841067(BasicNewsRecipe): ] remove_tags = [ - dict(name='ul', attrs={'class':["wj-share-buttons"]}), #Block social media + dict(name='ul', attrs={'class':['wj-share-buttons']}), #Block social media ] feeds = [ diff --git a/recipes/prospectmaguk_free.recipe b/recipes/prospectmaguk_free.recipe index c37513a90d..b0018324df 100644 --- a/recipes/prospectmaguk_free.recipe +++ b/recipes/prospectmaguk_free.recipe @@ -8,47 +8,47 @@ from urllib.parse import urljoin from calibre.web.feeds.news import BasicNewsRecipe, prefixed_classes -_issue_url = "" +_issue_url = '' class ProspectMagazineUKFree(BasicNewsRecipe): - title = "Prospect Magazine (Free)" - __author__ = "ping" + title = 'Prospect Magazine (Free)' + __author__ = 'ping' description = ( - "Prospect is Britain’s leading current affairs monthly magazine. " - "It is an independent and eclectic forum for writing and thinking—in " - "print and online. Published every month with two double issues in " - "the summer and winter, it spans politics, science, foreign affairs, " - "economics, the environment, philosophy and the arts." + 'Prospect is Britain’s leading current affairs monthly magazine. ' + 'It is an independent and eclectic forum for writing and thinking—in ' + 'print and online. Published every month with two double issues in ' + 'the summer and winter, it spans politics, science, foreign affairs, ' + 'economics, the environment, philosophy and the arts.' ) - language = "en_GB" - category = "news, UK" - publication_type = "magazine" - masthead_url = "https://media.prospectmagazine.co.uk/prod/images/gm_grid_thumbnail/358ffc17208c-f4c3cddcdeda-prospect-masthead.png" - encoding = "utf-8" + language = 'en_GB' + category = 'news, UK' + publication_type = 'magazine' + masthead_url = 'https://media.prospectmagazine.co.uk/prod/images/gm_grid_thumbnail/358ffc17208c-f4c3cddcdeda-prospect-masthead.png' + encoding = 'utf-8' remove_javascript = True no_stylesheets = True - ignore_duplicate_articles = {"url"} - INDEX = "https://www.prospectmagazine.co.uk/issues" + ignore_duplicate_articles = {'url'} + INDEX = 'https://www.prospectmagazine.co.uk/issues' - keep_only_tags = [dict(class_="prop-book-article-panel_main")] + keep_only_tags = [dict(class_='prop-book-article-panel_main')] remove_tags = [ dict( class_=[ - "prop-book-review-header-wrapper_magazine", - "prop-mobile-social-share_header", - "prop-magazine-link-block", - "pros-article-body__img-credit", - "pros-article-topics__wrapper", - "pros-article-author__image-wrapper", - "prop-book-review-promo_details-buy-mobile", + 'prop-book-review-header-wrapper_magazine', + 'prop-mobile-social-share_header', + 'prop-magazine-link-block', + 'pros-article-body__img-credit', + 'pros-article-topics__wrapper', + 'pros-article-author__image-wrapper', + 'prop-book-review-promo_details-buy-mobile', ] ), - dict(id=["disqus_thread", "newsletter_wrapper"]), - prefixed_classes("dfp-slot-"), + dict(id=['disqus_thread', 'newsletter_wrapper']), + prefixed_classes('dfp-slot-'), ] - extra_css = """ + extra_css = ''' h1 { font-size: 1.8rem; margin-bottom: 0.4rem; } .prop-book-review-header-wrapper_standfirst { font-size: 1.2rem; font-style: italic; font-weight: normal; margin-bottom: 0.5rem; } .prop-book-review-header-wrapper_details { margin-top: 1rem; margin-bottom: 1rem; } @@ -62,23 +62,23 @@ class ProspectMagazineUKFree(BasicNewsRecipe): .pullquote, blockquote { text-align: center; margin-left: 0; margin-bottom: 0.4rem; font-size: 1.25rem; } .prop-book-review-article_author { margin: 1.5rem 0; font-style: italic; } .prop-book-review-promo { margin-bottom: 1rem; } - """ + ''' def preprocess_html(self, soup): # re-position lede image - lede_img = soup.find("img", class_="prop-book-review-header-wrapper_image") - meta = soup.find("div", class_="prop-book-review-header-wrapper_details") + lede_img = soup.find('img', class_='prop-book-review-header-wrapper_image') + meta = soup.find('div', class_='prop-book-review-header-wrapper_details') if lede_img and meta: lede_img = lede_img.extract() meta.insert_after(lede_img) - for img in soup.find_all("img", attrs={"data-src": True}): - img["src"] = img["data-src"] - del img["data-src"] + for img in soup.find_all('img', attrs={'data-src': True}): + img['src'] = img['data-src'] + del img['data-src'] - for byline_link in soup.find_all("a", attrs={"data-author-name": True}): + for byline_link in soup.find_all('a', attrs={'data-author-name': True}): byline_link.unwrap() - for author_link in soup.find_all("a", class_="pros-article-author"): + for author_link in soup.find_all('a', class_='pros-article-author'): author_link.unwrap() return soup @@ -87,39 +87,39 @@ class ProspectMagazineUKFree(BasicNewsRecipe): if not _issue_url: issues_soup = self.index_to_soup(self.INDEX) curr_issue_a_ele = issues_soup.find( - "a", class_="pros-collection-landing__item" + 'a', class_='pros-collection-landing__item' ) - curr_issue_url = urljoin(self.INDEX, curr_issue_a_ele["href"]) + curr_issue_url = urljoin(self.INDEX, curr_issue_a_ele['href']) else: curr_issue_url = _issue_url soup = self.index_to_soup(curr_issue_url) issue_name = ( - self.tag_to_string(soup.find(class_="magazine-lhc__issue-name")) - .replace(" issue", "") + self.tag_to_string(soup.find(class_='magazine-lhc__issue-name')) + .replace(' issue', '') .strip() ) - self.timefmt = f" [{issue_name}]" + self.timefmt = f' [{issue_name}]' - self.cover_url = soup.find("img", class_="magazine-lhc__cover-image")[ - "data-src" - ].replace("portrait_small_fit", "portrait_large_fit") + self.cover_url = soup.find('img', class_='magazine-lhc__cover-image')[ + 'data-src' + ].replace('portrait_small_fit', 'portrait_large_fit') articles = OrderedDict() - sections = soup.find_all("div", class_="pro-magazine-section") + sections = soup.find_all('div', class_='pro-magazine-section') for section in sections: section_name = self.tag_to_string( - section.find(class_="pro-magazine-section__name") + section.find(class_='pro-magazine-section__name') ) for sect_article in section.find_all( - class_="pro-magazine-section__article" + class_='pro-magazine-section__article' ): articles.setdefault(section_name, []).append( { - "url": urljoin(self.INDEX, sect_article.find("a")["href"]), - "title": self.tag_to_string( + 'url': urljoin(self.INDEX, sect_article.find('a')['href']), + 'title': self.tag_to_string( sect_article.find( - class_="pro-magazine-section__article-headline" + class_='pro-magazine-section__article-headline' ) ), } diff --git a/recipes/psych.recipe b/recipes/psych.recipe index b5353a4f1e..b07ed764d5 100644 --- a/recipes/psych.recipe +++ b/recipes/psych.recipe @@ -5,10 +5,10 @@ from calibre.web.feeds.recipes import BasicNewsRecipe def absurl(url): - if url.startswith("//"): - return "https:" + url - if url.startswith("/"): - return "https://www.psychologytoday.com" + url + if url.startswith('//'): + return 'https:' + url + if url.startswith('/'): + return 'https://www.psychologytoday.com' + url return url diff --git a/recipes/quanta_magazine.recipe b/recipes/quanta_magazine.recipe index 404b7d5a50..aa9d7567fd 100644 --- a/recipes/quanta_magazine.recipe +++ b/recipes/quanta_magazine.recipe @@ -7,25 +7,25 @@ from calibre.web.feeds.news import BasicNewsRecipe class Quanta(BasicNewsRecipe): - title = "Quanta Magazine" + title = 'Quanta Magazine' __author__ = 'lui1' - description = "Articles from the magazine. Please set to download weekly." + description = 'Articles from the magazine. Please set to download weekly.' oldest_article = 7 max_articles_per_feed = 100 language = 'en' encoding = 'UTF-8' - publication_type = "blog" - cover_url = "https://d2r55xnwy6nx47.cloudfront.net/uploads/2017/05/logo.png" + publication_type = 'blog' + cover_url = 'https://d2r55xnwy6nx47.cloudfront.net/uploads/2017/05/logo.png' feeds = [ ('Articles', 'https://api.quantamagazine.org/feed/'), ] keep_only_tags = [ - dict(name="div", attrs={"id": "postBody"}), + dict(name='div', attrs={'id': 'postBody'}), ] remove_tags = [ - dict(name="div", attrs={"class": "post__sidebar__content"}), + dict(name='div', attrs={'class': 'post__sidebar__content'}), ] diff --git a/recipes/queueacmorg.recipe b/recipes/queueacmorg.recipe index e58878f435..deb7053020 100644 --- a/recipes/queueacmorg.recipe +++ b/recipes/queueacmorg.recipe @@ -11,17 +11,17 @@ ACM Queue Magazine class QueueAcmOrg(BasicNewsRecipe): - title = "ACM Queue Magazine" + title = 'ACM Queue Magazine' __author__ = 'yodha8' - description = "Queue is the ACM magazine for practicing software engineers. Published once every 2 months. Example: Jan-Feb." + description = 'Queue is the ACM magazine for practicing software engineers. Published once every 2 months. Example: Jan-Feb.' oldest_article = 60 max_articles_per_feed = 50 auto_cleanup = True language = 'en' - cover_url = "https://queue.acm.org/img/acmqueue_logo.gif" + cover_url = 'https://queue.acm.org/img/acmqueue_logo.gif' feeds = [ - ("All Queue Content", "https://queue.acm.org/rss/feeds/queuecontent.xml"), + ('All Queue Content', 'https://queue.acm.org/rss/feeds/queuecontent.xml'), ] def get_cover_url(self): diff --git a/recipes/readitlater.recipe b/recipes/readitlater.recipe index 43c8c8211c..5a45b93596 100644 --- a/recipes/readitlater.recipe +++ b/recipes/readitlater.recipe @@ -1,6 +1,6 @@ -""" +''' Pocket Calibre Recipe v1.5 -""" +''' import json import operator @@ -69,22 +69,22 @@ class Pocket(BasicNewsRecipe): br['password'] = self.password br.submit() else: - self.user_error("This Recipe requires authentication") + self.user_error('This Recipe requires authentication') return br def get_auth_uri(self): - """Quick function to return the authentication part of the url""" - uri = "" + '''Quick function to return the authentication part of the url''' + uri = '' uri = u'{0}&apikey={1!s}'.format(uri, self.apikey) if self.username is None or self.password is None: - self.user_error("Username or password is blank.") + self.user_error('Username or password is blank.') else: uri = u'{0}&username={1!s}'.format(uri, self.username) uri = u'{0}&password={1!s}'.format(uri, self.password) return uri def get_pull_articles_uri(self): - uri = "" + uri = '' uri = u'{0}&state={1}'.format(uri, u'unread') uri = u'{0}&contentType={1}'.format(uri, u'article') uri = u'{0}&sort={1}'.format(uri, self.sort_method) @@ -95,7 +95,7 @@ class Pocket(BasicNewsRecipe): def parse_index(self): pocket_feed = [] - fetch_url = u"{0}?{1}{2}".format( + fetch_url = u'{0}?{1}{2}'.format( self.read_api_url, self.get_auth_uri(), self.get_pull_articles_uri() @@ -106,7 +106,7 @@ class Pocket(BasicNewsRecipe): if len(pocket_feed) < self.minimum_articles: self.mark_as_read_after_dl = False self.user_error( - "Only {0} articles retrieved, minimum_articles not reached".format(len(pocket_feed))) + 'Only {0} articles retrieved, minimum_articles not reached'.format(len(pocket_feed))) for pocket_article in pocket_feed.items(): self.articles.append({ @@ -119,7 +119,7 @@ class Pocket(BasicNewsRecipe): 'sort': pocket_article[1]['sort_id'] }) self.articles = sorted(self.articles, key=operator.itemgetter('sort')) - return [("My Pocket Articles for {0}".format(strftime('[%I:%M %p]')), self.articles)] + return [('My Pocket Articles for {0}'.format(strftime('[%I:%M %p]')), self.articles)] def mark_as_read(self, mark_list): actions_list = [] diff --git a/recipes/real_clear.recipe b/recipes/real_clear.recipe index a2031e8b7b..4a36bf6b68 100644 --- a/recipes/real_clear.recipe +++ b/recipes/real_clear.recipe @@ -35,17 +35,17 @@ class RealClear(BasicNewsRecipe): # Numeric parameter is type, controls whether we look for feedsets = [ - ["Politics", "http://www.realclearpolitics.com/index.xml", 0], - ["Policy", "http://www.realclearpolicy.com/index.xml", 0], - ["Science", "http://www.realclearscience.com/index.xml", 0], - ["Tech", "http://www.realcleartechnology.com/index.xml", 0], + ['Politics', 'http://www.realclearpolitics.com/index.xml', 0], + ['Policy', 'http://www.realclearpolicy.com/index.xml', 0], + ['Science', 'http://www.realclearscience.com/index.xml', 0], + ['Tech', 'http://www.realcleartechnology.com/index.xml', 0], # The feedburner is essentially the same as the top feed, politics. # ["Politics Burner", "http://feeds.feedburner.com/realclearpolitics/qlMj", 1], # ["Commentary", "http://feeds.feedburner.com/Realclearpolitics-Articles", 1], - ["Markets Home", "http://www.realclearmarkets.com/index.xml", 0], - ["Markets", "http://www.realclearmarkets.com/articles/index.xml", 0], - ["World", "http://www.realclearworld.com/index.xml", 0], - ["World Blog", "http://www.realclearworld.com/blog/index.xml", 2] + ['Markets Home', 'http://www.realclearmarkets.com/index.xml', 0], + ['Markets', 'http://www.realclearmarkets.com/articles/index.xml', 0], + ['World', 'http://www.realclearworld.com/index.xml', 0], + ['World Blog', 'http://www.realclearworld.com/blog/index.xml', 2] ] # Hints to extractPrintURL. # First column is the URL snippet. Then the string to search for as text, @@ -53,13 +53,13 @@ class RealClear(BasicNewsRecipe): # drill down. phUrlSnip, phLinkText, phMainSearch, phHrefSearch = range(4) - printhints = [["realclear", "", '', 'printpage'], - ["billoreilly.com", "Print this entry", 'a', ''], - ["billoreilly.com", "Print This Article", 'a', ''], - ["politico.com", "Print", + printhints = [['realclear', '', '', 'printpage'], + ['billoreilly.com', 'Print this entry', 'a', ''], + ['billoreilly.com', 'Print This Article', 'a', ''], + ['politico.com', 'Print', 'a', 'share-print'], - ["nationalreview.com", ">Print<", 'a', ''], - ["reason.com", "", 'a', 'printer'] + ['nationalreview.com', '>Print<', 'a', ''], + ['reason.com', '', 'a', 'printer'] # The following are not supported due to JavaScripting, and would require obfuscated_article to handle # forbes, # usatoday - just prints with all current crap anyhow @@ -82,12 +82,12 @@ class RealClear(BasicNewsRecipe): def extractPrintURL(self, pageURL): tagURL = pageURL baseParse = urlparse(pageURL) - baseURL = baseParse[0] + "://" + baseParse[1] + baseURL = baseParse[0] + '://' + baseParse[1] hintsCount = len(self.printhints) for x in range(0, hintsCount): if pageURL.find(self.printhints[x][0]) == -1: continue - print("Trying " + self.printhints[x][0]) + print('Trying ' + self.printhints[x][0]) # Only retrieve the soup if we have a match to check for the # printed article with. soup = self.index_to_soup(pageURL) @@ -96,51 +96,51 @@ class RealClear(BasicNewsRecipe): if len(self.printhints[x][self.phHrefSearch]) > 0 and len(self.printhints[x][self.phLinkText]) == 0: # e.g. RealClear if self.debugMessages is True: - print("Search by href: " + + print('Search by href: ' + self.printhints[x][self.phHrefSearch]) printFind = soup.find(href=re.compile( self.printhints[x][self.phHrefSearch])) elif len(self.printhints[x][3]) > 0 and len(self.printhints[x][1]) == 0: if self.debugMessages is True: - print("Search 1: " + - self.printhints[x][2] + " Attributes: ") + print('Search 1: ' + + self.printhints[x][2] + ' Attributes: ') print(self.printhints[x][3]) printFind = soup.find( self.printhints[x][2], attrs=self.printhints[x][3]) elif len(self.printhints[x][3]) > 0: if self.debugMessages is True: - print("search2") + print('search2') printFind = soup.find(self.printhints[x][2], attrs=self.printhints[ x][3], text=self.printhints[x][1]) else: if self.debugMessages is True: print( - "Default Search: " + self.printhints[x][2] + " Text: " + self.printhints[x][1]) + 'Default Search: ' + self.printhints[x][2] + ' Text: ' + self.printhints[x][1]) printFind = soup.find( self.printhints[x][2], text=self.printhints[x][1]) if printFind is None: if self.debugMessages is True: - print("Not Found") + print('Not Found') # print(soup) - print("end soup\n\n") + print('end soup\n\n') continue print(printFind) if isinstance(printFind, NavigableString) is False: if printFind['href'] is not None: - print("Check " + printFind['href'] + - " for base of " + baseURL) - if printFind['href'].find("http") != 0: + print('Check ' + printFind['href'] + + ' for base of ' + baseURL) + if printFind['href'].find('http') != 0: return baseURL + printFind['href'] return printFind['href'] tag = printFind.parent print(tag) if tag.get('href', None) is None: if self.debugMessages is True: - print("Not in parent, trying skip-up") + print('Not in parent, trying skip-up') if tag.parent['href'] is None: if self.debugMessages is True: - print("Not in skip either, aborting") + print('Not in skip either, aborting') continue return tag.parent['href'] return tag['href'] @@ -148,45 +148,45 @@ class RealClear(BasicNewsRecipe): def get_browser(self): if self.debugMessages is True: - print("In get_browser") + print('In get_browser') br = BasicNewsRecipe.get_browser(self) return br def parseRSS(self, index): if self.debugMessages is True: - print("\n\nStarting " + self.feedsets[index][0]) + print('\n\nStarting ' + self.feedsets[index][0]) articleList = [] soup = self.index_to_soup(self.feedsets[index][1]) - for div in soup.findAll("item"): - title = div.find("title").contents[0] - urlEl = div.find("originalLink") + for div in soup.findAll('item'): + title = div.find('title').contents[0] + urlEl = div.find('originalLink') if urlEl is None or len(urlEl.contents) == 0: - urlEl = div.find("originallink") + urlEl = div.find('originallink') if urlEl is None or len(urlEl.contents) == 0: - urlEl = div.find("link") + urlEl = div.find('link') if urlEl is None or len(urlEl.contents) == 0: - urlEl = div.find("guid") + urlEl = div.find('guid') if urlEl is None or title is None or len(urlEl.contents) == 0: - print("Error in feed " + self.feedsets[index][0]) + print('Error in feed ' + self.feedsets[index][0]) print(div) continue print(title) print(urlEl) - url = urlEl.contents[0].encode("utf-8") - description = div.find("description") + url = urlEl.contents[0].encode('utf-8') + description = div.find('description') if description is not None and description.contents is not None and len(description.contents) > 0: description = description.contents[0] else: - description = "None" - pubDateEl = div.find("pubDate") + description = 'None' + pubDateEl = div.find('pubDate') if pubDateEl is None: - pubDateEl = div.find("pubdate") + pubDateEl = div.find('pubdate') if pubDateEl is None: pubDate = time.strftime('%a, %d %b') else: pubDate = pubDateEl.contents[0] if self.debugMessages is True: - print("Article") + print('Article') print(title) print(description) print(pubDate) diff --git a/recipes/regina_leader_post.recipe b/recipes/regina_leader_post.recipe index d3ff8ef484..d9b37fc9bc 100644 --- a/recipes/regina_leader_post.recipe +++ b/recipes/regina_leader_post.recipe @@ -123,24 +123,24 @@ class CanWestPaper(BasicNewsRecipe): continue break if daysback == 7: - self.log("\nCover unavailable") + self.log('\nCover unavailable') cover = None return cover def fixChars(self, string): # Replace lsquo (\x91) - fixed = re.sub("\x91", "‘", string) + fixed = re.sub('\x91', '‘', string) # Replace rsquo (\x92) - fixed = re.sub("\x92", "’", fixed) + fixed = re.sub('\x92', '’', fixed) # Replace ldquo (\x93) - fixed = re.sub("\x93", "“", fixed) + fixed = re.sub('\x93', '“', fixed) # Replace rdquo (\x94) - fixed = re.sub("\x94", "”", fixed) + fixed = re.sub('\x94', '”', fixed) # Replace ndash (\x96) - fixed = re.sub("\x96", "–", fixed) + fixed = re.sub('\x96', '–', fixed) # Replace mdash (\x97) - fixed = re.sub("\x97", "—", fixed) - fixed = re.sub("’", "’", fixed) + fixed = re.sub('\x97', '—', fixed) + fixed = re.sub('’', '’', fixed) return fixed def massageNCXText(self, description): @@ -180,14 +180,14 @@ class CanWestPaper(BasicNewsRecipe): ans = ['News'] # Find each instance of class="sectiontitle", class="featurecontent" - for divtag in soup.findAll('div', attrs={'class': ["section_title02", "featurecontent"]}): + for divtag in soup.findAll('div', attrs={'class': ['section_title02', 'featurecontent']}): if 'section_title' in ''.join(divtag['class']): # div contains section title if not divtag.h3: continue key = self.tag_to_string(divtag.h3, False) ans.append(key) - self.log("Section name %s" % key) + self.log('Section name %s' % key) continue # div contains article data h1tag = divtag.find('h1') diff --git a/recipes/respekt_magazine.recipe b/recipes/respekt_magazine.recipe index aa5f76e08d..ecb9cae42b 100644 --- a/recipes/respekt_magazine.recipe +++ b/recipes/respekt_magazine.recipe @@ -72,8 +72,8 @@ class respektRecipe(BasicNewsRecipe): # So that remove_tags_before works for this section def preprocess_raw_html(self, raw_html, url): root = lxml.html.fromstring(raw_html) - if root.xpath("//title")[0].text == (u"Respekt • Despekt • RESPEKT"): - raw_html = re.sub("h2","h1",raw_html) + if root.xpath('//title')[0].text == (u'Respekt • Despekt • RESPEKT'): + raw_html = re.sub('h2','h1',raw_html) return raw_html def parse_index(self): @@ -82,17 +82,17 @@ class respektRecipe(BasicNewsRecipe): current_edition_url = root1.xpath("//div[@class='heroissue']/a")[0].items()[0][1] raw2 = self.index_to_soup('https://www.respekt.cz/' + current_edition_url, raw=True) root2 = lxml.html.fromstring(raw2) - self.cover_url = root2.xpath("//i[contains(@class, 'heroissue-cover')]")[0].get("data-src") + self.cover_url = root2.xpath("//i[contains(@class, 'heroissue-cover')]")[0].get('data-src') # Fetch date date_text = root2.xpath("//time[@class='heroissue-date']")[0].text.split(',')[1] - s = date_text.split(" ") + s = date_text.split(' ') # Are the dates of the issue in the same month and year? if len(s) == 4 or len(s) == 7: - date = "/".join([s[1].split(".")[0],s[2].split(".")[0],s[-1]]) + date = '/'.join([s[1].split('.')[0],s[2].split('.')[0],s[-1]]) elif len(s) == 8: - date = "/".join([s[1].split(".")[0],s[2].split(".")[0],s[3]]) + date = '/'.join([s[1].split('.')[0],s[2].split('.')[0],s[3]]) self.conversion_options = {'pubdate':date} - self.title = "Respekt magazine #" + "/".join(current_edition_url.split("/")[-1:-3:-1]) + self.title = 'Respekt magazine #' + '/'.join(current_edition_url.split('/')[-1:-3:-1]) ans = [] for section in root2.xpath("//div[@class='col-md-6']/div[@class='issuedetail-categorized-sectionname']"): section_name = section.text @@ -100,7 +100,7 @@ class respektRecipe(BasicNewsRecipe): article = section.getnext() while hasattr(article, 'text') and not article.text.strip(): title = article.xpath("span[@class='issuedetail-categorized-title']")[0].text - url = respekt_url + article.xpath("@href")[0] + url = respekt_url + article.xpath('@href')[0] articles.append({'title':title,'url':url}) article = article.getnext() ans.append((section_name,articles)) @@ -113,24 +113,24 @@ class respektRecipe(BasicNewsRecipe): raw = u''.join(type(u'')(a) for a in soup.contents) root = lxml.html.fromstring(raw) # Fix Letem světem - if "Letem sv" in root.xpath("//title")[0].text: - p = root.xpath("//p") + if 'Letem sv' in root.xpath('//title')[0].text: + p = root.xpath('//p') for par in p[:]: next = par.getnext() if par.getchildren(): child = par.getchildren()[0] - if hasattr(next,"tag") and next.tag == "h2" and hasattr(child,"tag") and child.tag == "strong": + if hasattr(next,'tag') and next.tag == 'h2' and hasattr(child,'tag') and child.tag == 'strong': text = child.text_content() if next.text: - next.text = next.text + u" • " + text + next.text = next.text + u' • ' + text else: if next.getchildren(): next_child = next.getchildren()[0] - next_child.text = next_child.text + u" • " + text + next_child.text = next_child.text + u' • ' + text par.getparent().remove(par) # Insert text length text = root.xpath("//div[@id='postcontent']")[0] - article_length = u" • " + str(len(text.text_content().split(' '))) + ' slov' + article_length = u' • ' + str(len(text.text_content().split(' '))) + ' slov' try: aut = root.xpath("//div[@class='authorship-names']")[0] if aut.getchildren() and aut.getchildren()[0].tag == 'a': @@ -149,11 +149,11 @@ class respektRecipe(BasicNewsRecipe): except: pass # Make images visible - pictures = root.xpath("//picture") + pictures = root.xpath('//picture') for picture in pictures: - image = picture.xpath("//source")[0] - image_link = [a for a in image.get('srcset').split(' ') if a[:4] == "http"][-1] - e=E.img({"src":image_link}) + image = picture.xpath('//source')[0] + image_link = [a for a in image.get('srcset').split(' ') if a[:4] == 'http'][-1] + e=E.img({'src':image_link}) picture.getparent().replace(picture,e) # Properly indent paragraphs = root.xpath('//p') @@ -163,11 +163,11 @@ class respektRecipe(BasicNewsRecipe): prev = par.getprevious() # Do not indent after headings if hasattr(prev,'tag') and prev.tag not in ['h2', 'h3']: - par.attrib['class']="indent_first_line" + par.attrib['class']='indent_first_line' # Fix subtitle for Téma try: o = root.xpath("//p[@class='post-perex']")[0] - e = E.h2({"class":"post-subtitle"}) + e = E.h2({'class':'post-subtitle'}) e.text = o.text o.getparent().replace(o,e) except: diff --git a/recipes/reuters.recipe b/recipes/reuters.recipe index 50f260a944..b3ecf2e42c 100644 --- a/recipes/reuters.recipe +++ b/recipes/reuters.recipe @@ -33,11 +33,11 @@ class Reuters(BasicNewsRecipe): resolve_internal_links = True ignore_duplicate_articles = {'url', 'title'} - extra_css = """ + extra_css = ''' .label, .auth { font-size:small; color:#202020; } .figc { font-size:small; } img {display:block; margin:0 auto;} - """ + ''' recipe_specific_options = { 'days': { diff --git a/recipes/revista22.recipe b/recipes/revista22.recipe index 7d2a55b2f1..9fe631e7e2 100644 --- a/recipes/revista22.recipe +++ b/recipes/revista22.recipe @@ -33,7 +33,7 @@ class Volkskrant(BasicNewsRecipe): dict(id=['comments']), dict(name=['script', 'noscript', 'style']), ] - remove_attributes = ["class", "id", "name", "style"] + remove_attributes = ['class', 'id', 'name', 'style'] encoding = 'utf-8' no_stylesheets = True ignore_duplicate_articles = {'url'} diff --git a/recipes/revista_veintitres.recipe b/recipes/revista_veintitres.recipe index 4a992d78f5..4940889bd0 100644 --- a/recipes/revista_veintitres.recipe +++ b/recipes/revista_veintitres.recipe @@ -29,10 +29,10 @@ class Veintitres(BasicNewsRecipe): auto_cleanup = True auto_cleanup_keep = '//h1' resolve_internal_links = True - INDEX = "https://www.veintitres.com.ar" - extra_css = """ + INDEX = 'https://www.veintitres.com.ar' + extra_css = ''' img{margin-bottom: 0.8em} - """ + ''' conversion_options = { 'comment': description, diff --git a/recipes/rts.recipe b/recipes/rts.recipe index 08133c98f7..3ed5fba7d6 100644 --- a/recipes/rts.recipe +++ b/recipes/rts.recipe @@ -59,9 +59,9 @@ class RTS(BasicNewsRecipe): soup.html['xml:lang'] = self.lang soup.html['lang'] = self.lang mlang = new_tag(soup, 'meta', [ - ("http-equiv", "Content-Language"), ("content", self.lang)]) + ('http-equiv', 'Content-Language'), ('content', self.lang)]) mcharset = new_tag(soup, 'meta', [ - ("http-equiv", "Content-Type"), ("content", "text/html; charset=UTF-8")]) + ('http-equiv', 'Content-Type'), ('content', 'text/html; charset=UTF-8')]) soup.head.insert(0, mlang) soup.head.insert(1, mcharset) return self.adeify_images(soup) diff --git a/recipes/russiafeed.recipe b/recipes/russiafeed.recipe index a90067e869..514da5d158 100644 --- a/recipes/russiafeed.recipe +++ b/recipes/russiafeed.recipe @@ -25,11 +25,11 @@ class RussiaFeed(BasicNewsRecipe): publication_type = 'newsportal' auto_cleanup = True ignore_duplicate_articles = {'url'} - extra_css = """ + extra_css = ''' body{font-family: Roboto, Arial, sans-serif} img{margin-top:1em; margin-bottom: 1em; display:block} entry-title,entry-subtitle{font-family: Rajdhani, Poppins, Roboto, Arial, sans-serif} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/rzeczpospolita.recipe b/recipes/rzeczpospolita.recipe index e9e13e04ff..f05b321b3a 100644 --- a/recipes/rzeczpospolita.recipe +++ b/recipes/rzeczpospolita.recipe @@ -19,9 +19,9 @@ class RzeczpospolitaRecipe(BasicNewsRecipe): simultaneous_downloads = 5 feeds = [] - feeds.append((u"Wiadomości", u'http://www.rp.pl/rss/1056')) # Wydarzenia - feeds.append((u"Ekonomia", u'http://www.rp.pl/rss/1004')) # Ekonomia - feeds.append((u"Prawo", u'http://www.rp.pl/rss/1037')) # Prawo + feeds.append((u'Wiadomości', u'http://www.rp.pl/rss/1056')) # Wydarzenia + feeds.append((u'Ekonomia', u'http://www.rp.pl/rss/1004')) # Ekonomia + feeds.append((u'Prawo', u'http://www.rp.pl/rss/1037')) # Prawo keep_only_tags = [] keep_only_tags.append(dict(name='h1', attrs={'id': 'article-title'})) diff --git a/recipes/saskatoon_star_phoenix.recipe b/recipes/saskatoon_star_phoenix.recipe index 6592ef51aa..6b89e889c7 100644 --- a/recipes/saskatoon_star_phoenix.recipe +++ b/recipes/saskatoon_star_phoenix.recipe @@ -123,24 +123,24 @@ class CanWestPaper(BasicNewsRecipe): continue break if daysback == 7: - self.log("\nCover unavailable") + self.log('\nCover unavailable') cover = None return cover def fixChars(self, string): # Replace lsquo (\x91) - fixed = re.sub("\x91", "‘", string) + fixed = re.sub('\x91', '‘', string) # Replace rsquo (\x92) - fixed = re.sub("\x92", "’", fixed) + fixed = re.sub('\x92', '’', fixed) # Replace ldquo (\x93) - fixed = re.sub("\x93", "“", fixed) + fixed = re.sub('\x93', '“', fixed) # Replace rdquo (\x94) - fixed = re.sub("\x94", "”", fixed) + fixed = re.sub('\x94', '”', fixed) # Replace ndash (\x96) - fixed = re.sub("\x96", "–", fixed) + fixed = re.sub('\x96', '–', fixed) # Replace mdash (\x97) - fixed = re.sub("\x97", "—", fixed) - fixed = re.sub("’", "’", fixed) + fixed = re.sub('\x97', '—', fixed) + fixed = re.sub('’', '’', fixed) return fixed def massageNCXText(self, description): @@ -180,14 +180,14 @@ class CanWestPaper(BasicNewsRecipe): ans = ['News'] # Find each instance of class="sectiontitle", class="featurecontent" - for divtag in soup.findAll('div', attrs={'class': ["section_title02", "featurecontent"]}): + for divtag in soup.findAll('div', attrs={'class': ['section_title02', 'featurecontent']}): if ''.join(divtag['class']).startswith('section_title'): # div contains section title if not divtag.h3: continue key = self.tag_to_string(divtag.h3, False) ans.append(key) - self.log("Section name %s" % key) + self.log('Section name %s' % key) continue # div contains article data h1tag = divtag.find('h1') diff --git a/recipes/science_news.recipe b/recipes/science_news.recipe index f3de3c20b5..50198a3d19 100644 --- a/recipes/science_news.recipe +++ b/recipes/science_news.recipe @@ -12,8 +12,8 @@ from calibre.web.feeds.news import BasicNewsRecipe, prefixed_classes class ScienceNewsIssue(BasicNewsRecipe): title = u'Science News' - description = ("Science News is an award-winning bi-weekly newsmagazine covering the most important research" - " in all fields of science. This recipe downloads all the articles from the latest issue.") + description = ('Science News is an award-winning bi-weekly newsmagazine covering the most important research' + ' in all fields of science. This recipe downloads all the articles from the latest issue.') category = u'Science, Technology, News' publisher = u'Society for Science & the Public' language = 'en' @@ -54,16 +54,16 @@ class ScienceNewsIssue(BasicNewsRecipe): # Get articles soup = self.index_to_soup(url) soup = soup.find('main', attrs={'id':'content'}) - re_article = re.compile("https://www.sciencenews.org/article/") + re_article = re.compile('https://www.sciencenews.org/article/') stories = [] past_urls = set() for sec in soup.find_all(href=re_article): - article_url = sec["href"] + article_url = sec['href'] article_title = sec.text.strip() # Ignore image URLs which do not have text title - if article_title == "": + if article_title == '': continue # Ignore if link is a duplicate @@ -73,12 +73,12 @@ class ScienceNewsIssue(BasicNewsRecipe): past_urls.add(article_url) self.log('\t', article_title, ' ', article_url) article_info = { - "url": article_url, - "title": article_title, + 'url': article_url, + 'title': article_title, } stories.append(article_info) index = [ - ("Articles", stories), + ('Articles', stories), ] return index diff --git a/recipes/scientific_american.recipe b/recipes/scientific_american.recipe index 4a0374ec70..07b7c544e4 100644 --- a/recipes/scientific_american.recipe +++ b/recipes/scientific_american.recipe @@ -1,5 +1,5 @@ #!/usr/bin/env python -__license__ = "GPL v3" +__license__ = 'GPL v3' import json from datetime import datetime @@ -9,29 +9,29 @@ from calibre.web.feeds.news import BasicNewsRecipe, prefixed_classes class ScientificAmerican(BasicNewsRecipe): - title = "Scientific American" - description = "Popular Science. Monthly magazine. Should be downloaded around the middle of each month." - category = "science" - __author__ = "Kovid Goyal" + title = 'Scientific American' + description = 'Popular Science. Monthly magazine. Should be downloaded around the middle of each month.' + category = 'science' + __author__ = 'Kovid Goyal' no_stylesheets = True - language = "en" - publisher = "Nature Publishing Group" + language = 'en' + publisher = 'Nature Publishing Group' remove_empty_feeds = True remove_javascript = True - timefmt = " [%B %Y]" - remove_attributes = ["height", "width"] + timefmt = ' [%B %Y]' + remove_attributes = ['height', 'width'] masthead_url = ( - "https://static.scientificamerican.com/sciam/assets/Image/newsletter/salogo.png" + 'https://static.scientificamerican.com/sciam/assets/Image/newsletter/salogo.png' ) - extra_css = """ + extra_css = ''' [class^="article_dek-"] { font-style:italic; color:#202020; } [class^="article_authors-"] {font-size:small; color:#202020; } [class^="article__image-"], [class^="lead_image-"], .calibre-nuked-tag-figcaption { font-size:small; } [class^="bio-"] { font-size:small; color:#404040; } em, blockquote { color:#202020; } - """ + ''' - needs_subscription = "optional" + needs_subscription = 'optional' keep_only_tags = [ prefixed_classes( @@ -59,10 +59,10 @@ class ScientificAmerican(BasicNewsRecipe): def get_browser(self, *args): br = BasicNewsRecipe.get_browser(self) if self.username and self.password: - br.open("https://www.scientificamerican.com/account/login/") - br.select_form(predicate=lambda f: f.attrs.get("id") == "login") - br["emailAddress"] = self.username - br["password"] = self.password + br.open('https://www.scientificamerican.com/account/login/') + br.select_form(predicate=lambda f: f.attrs.get('id') == 'login') + br['emailAddress'] = self.username + br['password'] = self.password br.submit() return br @@ -87,50 +87,50 @@ class ScientificAmerican(BasicNewsRecipe): if d and isinstance(d, str): issue = d else: - fp_soup = self.index_to_soup("https://www.scientificamerican.com") + fp_soup = self.index_to_soup('https://www.scientificamerican.com') curr_issue_link = fp_soup.find(**prefixed_classes('latest_issue_links-')) if not curr_issue_link: - self.abort_recipe_processing("Unable to find issue link") - issue = 'https://www.scientificamerican.com' + curr_issue_link.a["href"] + self.abort_recipe_processing('Unable to find issue link') + issue = 'https://www.scientificamerican.com' + curr_issue_link.a['href'] soup = self.index_to_soup(issue) - script = soup.find("script", id="__DATA__") + script = soup.find('script', id='__DATA__') if not script: - self.abort_recipe_processing("Unable to find script") + self.abort_recipe_processing('Unable to find script') - JSON = script.contents[0].split('JSON.parse(`')[1].replace("\\\\", "\\") + JSON = script.contents[0].split('JSON.parse(`')[1].replace('\\\\', '\\') data = json.JSONDecoder().raw_decode(JSON)[0] issue_info = ( data - .get("initialData", {}) - .get("issueData", {}) + .get('initialData', {}) + .get('issueData', {}) ) if not issue_info: - self.abort_recipe_processing("Unable to find issue info") + self.abort_recipe_processing('Unable to find issue info') - self.cover_url = issue_info["image_url"] + "?w=800" + self.cover_url = issue_info['image_url'] + '?w=800' - edition_date = datetime.strptime(issue_info["issue_date"], "%Y-%m-%d") - self.timefmt = f" [{edition_date:%B %Y}]" + edition_date = datetime.strptime(issue_info['issue_date'], '%Y-%m-%d') + self.timefmt = f' [{edition_date:%B %Y}]' feeds = {} - for section in issue_info.get("article_previews", {}): - for article in issue_info.get("article_previews", {}).get(section, []): - self.log('\t', article["title"]) + for section in issue_info.get('article_previews', {}): + for article in issue_info.get('article_previews', {}).get(section, []): + self.log('\t', article['title']) if section.startswith('featur'): feed_name = section.capitalize() else: - feed_name = article["category"] + feed_name = article['category'] if feed_name not in feeds: feeds[feed_name] = [] feeds[feed_name].append( { - "title": article["title"], - "url": urljoin( - "https://www.scientificamerican.com/article/", - article["slug"], + 'title': article['title'], + 'url': urljoin( + 'https://www.scientificamerican.com/article/', + article['slug'], ), - "description": article["summary"], + 'description': article['summary'], } ) sorted_feeds = dict(sorted(feeds.items(), key=lambda x: (not x[0].startswith('Featur'), x[0]))) diff --git a/recipes/scmp.recipe b/recipes/scmp.recipe index b8012e119e..8856c1543f 100644 --- a/recipes/scmp.recipe +++ b/recipes/scmp.recipe @@ -1,7 +1,7 @@ #!/usr/bin/env python -""" +''' scmp.com -""" +''' import json import time @@ -74,8 +74,8 @@ def load_article_from_json(raw, root): class SCMP(BasicNewsRecipe): - title = "South China Morning Post" - __author__ = "unkn0wn" + title = 'South China Morning Post' + __author__ = 'unkn0wn' description = ( 'The South China Morning Post is a leading news media company that has reported on China and Asia ' 'for more than a century with global impact. Founded in 1903, SCMP is headquartered in Hong Kong, ' @@ -84,18 +84,18 @@ class SCMP(BasicNewsRecipe): 'and inspiring through journalism of the highest standards. Our vision is to “Elevate Thought”, ' 'and our mission is to “Lead the global conversation about China”.' ) - publisher = "South China Morning Post Publishers Ltd." + publisher = 'South China Morning Post Publishers Ltd.' oldest_article = 1 no_stylesheets = True remove_javascript = True remove_attributes = ['width', 'height'] - encoding = "utf-8" + encoding = 'utf-8' use_embedded_content = False - language = "en_HK" + language = 'en_HK' remove_empty_feeds = True resolve_internal_links = True - publication_type = "newspaper" - ignore_duplicate_articles = {"title", "url"} + publication_type = 'newspaper' + ignore_duplicate_articles = {'title', 'url'} extra_css = 'blockquote, em { color: #202020; }' masthead_url = 'https://upload.wikimedia.org/wikipedia/commons/c/c3/SCMP_logo.svg' @@ -139,29 +139,29 @@ class SCMP(BasicNewsRecipe): remove_tags = [ dict( classes( - "sticky-wrap relative social-media social-media--extended__shares" - " article-body-comment scmp_button_comment_wrapper social-media--extended__in-site" - " footer scmp-advert-tile sidebar-col related-article share-widget" + 'sticky-wrap relative social-media social-media--extended__shares' + ' article-body-comment scmp_button_comment_wrapper social-media--extended__in-site' + ' footer scmp-advert-tile sidebar-col related-article share-widget' ) ), - dict(attrs={"addthis_title": True}), - dict(name=["script", "style"]), + dict(attrs={'addthis_title': True}), + dict(name=['script', 'style']), ] # https://www.scmp.com/rss feeds = [ - ("Hong Kong", "https://www.scmp.com/rss/2/feed"), - ("China", "https://www.scmp.com/rss/4/feed"), - ("Asia", "https://www.scmp.com/rss/3/feed"), - ("World", "https://www.scmp.com/rss/5/feed"), - ("Business", "https://www.scmp.com/rss/92/feed"), - ("Tech", "https://www.scmp.com/rss/36/feed"), - ("Life", "https://www.scmp.com/rss/94/feed"), - ("Culture", "https://www.scmp.com/rss/322296/feed"), - ("Sport", "https://www.scmp.com/rss/95/feed"), - ("Post Mag", "https://www.scmp.com/rss/71/feed"), - ("Style", "https://www.scmp.com/rss/72/feed"), - ("News", 'https://www.scmp.com/rss/91/feed') + ('Hong Kong', 'https://www.scmp.com/rss/2/feed'), + ('China', 'https://www.scmp.com/rss/4/feed'), + ('Asia', 'https://www.scmp.com/rss/3/feed'), + ('World', 'https://www.scmp.com/rss/5/feed'), + ('Business', 'https://www.scmp.com/rss/92/feed'), + ('Tech', 'https://www.scmp.com/rss/36/feed'), + ('Life', 'https://www.scmp.com/rss/94/feed'), + ('Culture', 'https://www.scmp.com/rss/322296/feed'), + ('Sport', 'https://www.scmp.com/rss/95/feed'), + ('Post Mag', 'https://www.scmp.com/rss/71/feed'), + ('Style', 'https://www.scmp.com/rss/72/feed'), + ('News', 'https://www.scmp.com/rss/91/feed') ] def print_version(self, url): diff --git a/recipes/scprint.recipe b/recipes/scprint.recipe index e4a2edc3b2..c7497aa6f3 100644 --- a/recipes/scprint.recipe +++ b/recipes/scprint.recipe @@ -5,7 +5,7 @@ class SCPrintMagazine(BasicNewsRecipe): title = u'SC Print Magazine' __author__ = u'Tony Maro' description = u'Last print version of the data security magazine' - INDEX = "http://www.scmagazineus.com/issuearchive/" + INDEX = 'http://www.scmagazineus.com/issuearchive/' no_stylesheets = True language = 'en' keep_only_tags = [dict(id=['article', 'review'])] @@ -52,9 +52,9 @@ class SCPrintMagazine(BasicNewsRecipe): if mylink.get('href'): artlink = mylink['href'] artlink = artlink.replace( - "/article", "/printarticle") + '/article', '/printarticle') artlink = artlink.replace( - "/review", "/printreview") + '/review', '/printreview') deck = onearticle.find( 'div', attrs={'class': 'deck'}) if deck is not None: @@ -72,7 +72,7 @@ class SCPrintMagazine(BasicNewsRecipe): br['ctl00$ctl00$cphAllPageContent$cphMainContent$SubscriberEasyLoginView1$txtEmail'] = self.username br['ctl00$ctl00$cphAllPageContent$cphMainContent$SubscriberEasyLoginView1$txtPassword'] = self.password raw = br.submit( - "ctl00$ctl00$cphAllPageContent$cphMainContent$SubscriberEasyLoginView1$btnLogin").read() + 'ctl00$ctl00$cphAllPageContent$cphMainContent$SubscriberEasyLoginView1$btnLogin').read() if 'Logout' not in raw: raise LoginFailed( _('Failed to log in, check your username and password for' diff --git a/recipes/screen_rant.recipe b/recipes/screen_rant.recipe index 0df33a3337..9ffc250503 100644 --- a/recipes/screen_rant.recipe +++ b/recipes/screen_rant.recipe @@ -7,7 +7,7 @@ class AdvancedUserRecipe1716109928(BasicNewsRecipe): title = 'Screen Rant' description = ( 'ScreenRant is a digital publication. ScreenRant has grown into one of the' - " world’s most prominent entertainment news sources. ScreenRant don’t just break and report news;" + ' world’s most prominent entertainment news sources. ScreenRant don’t just break and report news;' ' they analyze and editorialize it with unique insight and inside information.') __author__ = 'Spicy Poison' encoding = 'utf-8' diff --git a/recipes/seminar_magazine.recipe b/recipes/seminar_magazine.recipe index 7fc77f46d0..0737a9dce9 100644 --- a/recipes/seminar_magazine.recipe +++ b/recipes/seminar_magazine.recipe @@ -22,7 +22,7 @@ class Seminar(BasicNewsRecipe): soup = self.index_to_soup('https://www.india-seminar.com/') citem = soup.find('img', src=lambda x: x and 'covers' in x) if citem: - cover_url = "https://www.india-seminar.com/" + citem['src'] + cover_url = 'https://www.india-seminar.com/' + citem['src'] return cover_url def parse_index(self): diff --git a/recipes/sign_of_the_times.recipe b/recipes/sign_of_the_times.recipe index c29678fd8f..f6a1dc3e11 100644 --- a/recipes/sign_of_the_times.recipe +++ b/recipes/sign_of_the_times.recipe @@ -10,11 +10,11 @@ class SignOfTheTimes(BasicNewsRecipe): max_articles_per_feed = 50 use_embedded_content = False - extra_css = """ + extra_css = ''' h2{font-size: large; margin: .2em 0; text-decoration: none;} .image-caption{font-size: medium; font-style:italic; margin: 0 0 1em 0;} .article-info{font-size: small; font-style:italic; margin: 0 0 .5em 0;} - """ + ''' remove_stylesheets = True remove_tags = [ diff --git a/recipes/singtaohk.recipe b/recipes/singtaohk.recipe index 9372f3a9b1..abfb386697 100644 --- a/recipes/singtaohk.recipe +++ b/recipes/singtaohk.recipe @@ -5,7 +5,7 @@ from calibre.web.feeds.news import BasicNewsRecipe, classes class STHKRecipe(BasicNewsRecipe): title = '星島日報 (香港)' __author__ = 'unkn0wn' - description = 'The Sing Tao Daily is among Hong Kong\'s oldest Chinese language newspapers. (https://std.stheadline.com/)' + description = "The Sing Tao Daily is among Hong Kong's oldest Chinese language newspapers. (https://std.stheadline.com/)" category = 'Chinese, News, Hong Kong' language = 'zh' encoding = 'utf-8' diff --git a/recipes/skeptical_enquirer.recipe b/recipes/skeptical_enquirer.recipe index af22b4fcb4..d3cce85add 100644 --- a/recipes/skeptical_enquirer.recipe +++ b/recipes/skeptical_enquirer.recipe @@ -25,14 +25,14 @@ class FreeInquiry(BasicNewsRecipe): ignore_duplicate_articles = {'url'} remove_empty_feeds = True needs_subscription = True - extra_css = """ + extra_css = ''' .entry-header{ text-transform: uppercase; vertical-align: baseline; display: inline; } ul li{display: inline} - """ + ''' remove_tags = [ classes( diff --git a/recipes/smh.recipe b/recipes/smh.recipe index b732905229..ce1f422889 100644 --- a/recipes/smh.recipe +++ b/recipes/smh.recipe @@ -1,50 +1,50 @@ -__license__ = "GPL v3" -__copyright__ = "2010-2011, Darko Miletic " -""" +__license__ = 'GPL v3' +__copyright__ = '2010-2011, Darko Miletic ' +''' smh.com.au -""" +''' from calibre.web.feeds.news import BasicNewsRecipe class Smh_au(BasicNewsRecipe): - title = "The Sydney Morning Herald" - __author__ = "Darko Miletic" - description = "Breaking news from Sydney, Australia and the world. Features the latest business, sport, entertainment, travel, lifestyle, and technology news." # noqa: E501 - publisher = "Fairfax Digital" - category = "news, politics, Australia, Sydney" + title = 'The Sydney Morning Herald' + __author__ = 'Darko Miletic' + description = 'Breaking news from Sydney, Australia and the world. Features the latest business, sport, entertainment, travel, lifestyle, and technology news.' # noqa: E501 + publisher = 'Fairfax Digital' + category = 'news, politics, Australia, Sydney' oldest_article = 2 max_articles_per_feed = 200 no_stylesheets = True - ignore_duplicate_articles = {"title", "url"} + ignore_duplicate_articles = {'title', 'url'} use_embedded_content = False - encoding = "utf-8" + encoding = 'utf-8' - language = "en_AU" + language = 'en_AU' remove_empty_feeds = True - masthead_url = "http://images.smh.com.au/2010/02/02/1087188/smh-620.jpg" - publication_type = "newspaper" + masthead_url = 'http://images.smh.com.au/2010/02/02/1087188/smh-620.jpg' + publication_type = 'newspaper' - keep_only_tags = [dict(name="article")] + keep_only_tags = [dict(name='article')] remove_tags = [ - dict(name=["button"]), - dict(id=["saveTooltip"]), - dict(attrs={"class": "noPrint"}), + dict(name=['button']), + dict(id=['saveTooltip']), + dict(attrs={'class': 'noPrint'}), ] # https://www.smh.com.au/rssheadlines feeds = [ - ("Latest News", "https://www.smh.com.au/rss/feed.xml"), - ("Federal Politics", "https://www.smh.com.au/rss/politics/federal.xml"), - ("NSW News", "https://www.smh.com.au/rss/national/nsw.xml"), - ("World", "https://www.smh.com.au/rss/world.xml"), - ("National", "https://www.smh.com.au/rss/national.xml"), - ("Business", "https://www.smh.com.au/rss/business.xml"), - ("Culture", "https://www.smh.com.au/rss/culture.xml"), - ("Technology", "https://www.smh.com.au/rss/technology.xml"), - ("Environment", "https://www.smh.com.au/rss/environment.xml"), - ("Lifestyle", "https://www.smh.com.au/rss/lifestyle.xml"), - ("Property", "https://www.smh.com.au/rss/property.xml"), - ("Sport", "https://www.smh.com.au/rss/sport.xml"), - ("Ruby League", "https://www.smh.com.au/rss/sport/nrl.xml"), - ("AFL", "https://www.smh.com.au/rss/sport/afl.xml"), + ('Latest News', 'https://www.smh.com.au/rss/feed.xml'), + ('Federal Politics', 'https://www.smh.com.au/rss/politics/federal.xml'), + ('NSW News', 'https://www.smh.com.au/rss/national/nsw.xml'), + ('World', 'https://www.smh.com.au/rss/world.xml'), + ('National', 'https://www.smh.com.au/rss/national.xml'), + ('Business', 'https://www.smh.com.au/rss/business.xml'), + ('Culture', 'https://www.smh.com.au/rss/culture.xml'), + ('Technology', 'https://www.smh.com.au/rss/technology.xml'), + ('Environment', 'https://www.smh.com.au/rss/environment.xml'), + ('Lifestyle', 'https://www.smh.com.au/rss/lifestyle.xml'), + ('Property', 'https://www.smh.com.au/rss/property.xml'), + ('Sport', 'https://www.smh.com.au/rss/sport.xml'), + ('Ruby League', 'https://www.smh.com.au/rss/sport/nrl.xml'), + ('AFL', 'https://www.smh.com.au/rss/sport/afl.xml'), ] diff --git a/recipes/sol_haber.recipe b/recipes/sol_haber.recipe index 832d512974..164ff1da5f 100644 --- a/recipes/sol_haber.recipe +++ b/recipes/sol_haber.recipe @@ -69,7 +69,7 @@ class SolHaberRecipe(BasicNewsRecipe): dict(name='div', attrs={'class': re.compile(storybody_reg_exp, re.IGNORECASE)})] def get_masthead_title(self): - return self.title + "(" + self.end_date + ")" + return self.title + '(' + self.end_date + ')' def parse_index(self): diff --git a/recipes/star_gazetesi.recipe b/recipes/star_gazetesi.recipe index 86a7b9955a..4a4243e964 100644 --- a/recipes/star_gazetesi.recipe +++ b/recipes/star_gazetesi.recipe @@ -19,37 +19,37 @@ class Star (BasicNewsRecipe): compress_news_images = True # h1 and h2 tag classes respectively - extra_css = ".title-1 {font-size: 20px; color: #ed0000; text-align: center;}" - extra_css += ".title-2 {font-size: 16px;}" + extra_css = '.title-1 {font-size: 20px; color: #ed0000; text-align: center;}' + extra_css += '.title-2 {font-size: 16px;}' keep_only_tags = [ - dict(name="h1", attrs={"class": "title-1"}), - dict(name="h2", attrs={"class": "title-2"}), - dict(name="div", attrs={"class": "time"}), - dict(name="img", attrs={"class": "margin-bottom-lg"}), - dict(name="div", attrs={"class": "detay"}), + dict(name='h1', attrs={'class': 'title-1'}), + dict(name='h2', attrs={'class': 'title-2'}), + dict(name='div', attrs={'class': 'time'}), + dict(name='img', attrs={'class': 'margin-bottom-lg'}), + dict(name='div', attrs={'class': 'detay'}), ] feeds = [ - ("MANSET", "http://www.star.com.tr/rss/mansetler.xml"), - ("GÜNCEL", "http://www.star.com.tr/rss/guncel.xml"), - ("POLİTİKA", "http://www.star.com.tr/rss/politika.xml"), - ("EKONOMİ", "http://www.star.com.tr/rss/ekonomi.xml"), - ("DÜNYA", "http://www.star.com.tr/rss/dunya.xml"), - ("SON DAKİKA", "http://www.star.com.tr/rss/sondakika.xml"), - ("YAZARLAR", "http://www.star.com.tr/rss/yazarlar.xml"), - ("SPOR", "http://www.star.com.tr/rss/spor.xml"), - ("SİNEMA", "http://www.star.com.tr/rss/sinema.xml"), - ("SANAT", "http://www.star.com.tr/rss/sanat.xml"), - ("MAGAZİN", "http://www.star.com.tr/rss/magazin.xml"), - ("MEDYA", "http://www.star.com.tr/rss/medya.xml"), - ("SAĞLIK", "http://www.star.com.tr/rss/saglik.xml"), - ("TEKNOLOJİ", "http://www.star.com.tr/rss/teknoloji.xml"), - ("AÇIK GÖRÜŞ", "http://www.star.com.tr/rss/acikgorus.xml"), - ("PAZAR", "http://www.star.com.tr/rss/pazar.xml"), - ("CUMARTESİ", "http://www.star.com.tr/rss/cumartesi.xml"), - ("DİZİ", "http://www.star.com.tr/rss/dizi.xml"), - ("ANKARA", "http://www.star.com.tr/rss/ankara.xml"), - ("MEMURLAR", "http://www.star.com.tr/rss/memurlar.xml"), - ("OTO HAYAT", "http://www.star.com.tr/rss/otohayat.xml"), + ('MANSET', 'http://www.star.com.tr/rss/mansetler.xml'), + ('GÜNCEL', 'http://www.star.com.tr/rss/guncel.xml'), + ('POLİTİKA', 'http://www.star.com.tr/rss/politika.xml'), + ('EKONOMİ', 'http://www.star.com.tr/rss/ekonomi.xml'), + ('DÜNYA', 'http://www.star.com.tr/rss/dunya.xml'), + ('SON DAKİKA', 'http://www.star.com.tr/rss/sondakika.xml'), + ('YAZARLAR', 'http://www.star.com.tr/rss/yazarlar.xml'), + ('SPOR', 'http://www.star.com.tr/rss/spor.xml'), + ('SİNEMA', 'http://www.star.com.tr/rss/sinema.xml'), + ('SANAT', 'http://www.star.com.tr/rss/sanat.xml'), + ('MAGAZİN', 'http://www.star.com.tr/rss/magazin.xml'), + ('MEDYA', 'http://www.star.com.tr/rss/medya.xml'), + ('SAĞLIK', 'http://www.star.com.tr/rss/saglik.xml'), + ('TEKNOLOJİ', 'http://www.star.com.tr/rss/teknoloji.xml'), + ('AÇIK GÖRÜŞ', 'http://www.star.com.tr/rss/acikgorus.xml'), + ('PAZAR', 'http://www.star.com.tr/rss/pazar.xml'), + ('CUMARTESİ', 'http://www.star.com.tr/rss/cumartesi.xml'), + ('DİZİ', 'http://www.star.com.tr/rss/dizi.xml'), + ('ANKARA', 'http://www.star.com.tr/rss/ankara.xml'), + ('MEMURLAR', 'http://www.star.com.tr/rss/memurlar.xml'), + ('OTO HAYAT', 'http://www.star.com.tr/rss/otohayat.xml'), ] diff --git a/recipes/strange_horizons.recipe b/recipes/strange_horizons.recipe index d5ff6dcb88..1fd922b2c1 100644 --- a/recipes/strange_horizons.recipe +++ b/recipes/strange_horizons.recipe @@ -56,7 +56,7 @@ class StrangeHorizons(BasicNewsRecipe): desc = '' if exp := ti.find_next_sibling(**classes('excerpt')): desc = self.tag_to_string(exp) + desc - desc = re.sub(r"\d{5} ", "", desc) + desc = re.sub(r'\d{5} ', '', desc) if auth := ti.find_next_sibling(**classes('author')): desc = self.tag_to_string(auth) + ' | ' + desc @@ -64,5 +64,5 @@ class StrangeHorizons(BasicNewsRecipe): continue self.log(sec, '\n\t', title, '\n\t', desc, '\n\t\t', url) - feeds_dict[sec].append({"title": title, "url": url, "description": desc}) + feeds_dict[sec].append({'title': title, 'url': url, 'description': desc}) return [(section, articles) for section, articles in feeds_dict.items()] diff --git a/recipes/taz_rss.recipe b/recipes/taz_rss.recipe index 8b256afc46..b9e497ca77 100644 --- a/recipes/taz_rss.recipe +++ b/recipes/taz_rss.recipe @@ -47,7 +47,7 @@ class TazRSSRecipe(BasicNewsRecipe): no_stylesheets = True # default value is False, but True makes process much faster keep_only_tags = [ dict(name=['div'], attrs={ - 'class': re.compile(r".*\bsect_article\b.*")}) + 'class': re.compile(r'.*\bsect_article\b.*')}) ] remove_tags = [ dict(name=['div'], attrs={'class': 'sectfoot'}), diff --git a/recipes/telepolis.recipe b/recipes/telepolis.recipe index a62cccbb79..3df56c972e 100644 --- a/recipes/telepolis.recipe +++ b/recipes/telepolis.recipe @@ -22,4 +22,4 @@ class Telepolis(BasicNewsRecipe): remove_tags = [dict(name='p', attrs={'class':'printversion__back-to-article printversion--hide'})] def get_article_url(self, article): - return article.link + "&view=print" + return article.link + '&view=print' diff --git a/recipes/thairath.recipe b/recipes/thairath.recipe index 0fe46d6faf..d097d09927 100644 --- a/recipes/thairath.recipe +++ b/recipes/thairath.recipe @@ -57,4 +57,4 @@ class AdvancedUserRecipe1271637235(BasicNewsRecipe): remove_tags.append(dict(name='id', attrs={'class': 'footer'})) remove_tags.append( - dict(name="ul", attrs={'id': 'banner-highlights-images'})) + dict(name='ul', attrs={'id': 'banner-highlights-images'})) diff --git a/recipes/the_diplomat.recipe b/recipes/the_diplomat.recipe index fde4a0f1a7..a386d3bf6b 100644 --- a/recipes/the_diplomat.recipe +++ b/recipes/the_diplomat.recipe @@ -27,7 +27,7 @@ class Diplomat(BasicNewsRecipe): soup = self.index_to_soup('https://thediplomat.com') tag = soup.find(attrs={'class': 'td-nav-mag'}) if tag: - url = tag.find('img')['src'].split("/")[-1] + url = tag.find('img')['src'].split('/')[-1] self.cover_url = ('https://magazine.thediplomat.com/media/1080/' + url) # /ads/magazine/cover/td-mag-s-1/issue_89_cover.jpg @@ -67,5 +67,5 @@ class Diplomat(BasicNewsRecipe): def preprocess_html(self, soup): for img in soup.findAll('img', attrs={'src': True}): - img['src'] = img['src'].replace("td-story-s-1", "td-story-s-2") + img['src'] = img['src'].replace('td-story-s-1', 'td-story-s-2') return soup diff --git a/recipes/the_federalist.recipe b/recipes/the_federalist.recipe index 6fa4010168..d77f27b13c 100644 --- a/recipes/the_federalist.recipe +++ b/recipes/the_federalist.recipe @@ -23,10 +23,10 @@ class Federalist(BasicNewsRecipe): use_embedded_content = False remove_attributes = ['xmlns', 'lang', 'style', 'width', 'height'] - extra_css = """ + extra_css = ''' .shortbio,.article-excerpt{font-style: italic} .article-author-details,.article-author-description,.article-meta-author,.article-meta-date,.article-thumbnail-caption{font-size: small} - """ + ''' keep_only_tags = [ classes( diff --git a/recipes/the_nation.recipe b/recipes/the_nation.recipe index 20a4430cb3..ba10ce0ded 100644 --- a/recipes/the_nation.recipe +++ b/recipes/the_nation.recipe @@ -28,11 +28,11 @@ class Thenation(BasicNewsRecipe): login_url = 'http://www.thenation.com/user?destination=%3Cfront%3E' publication_type = 'magazine' needs_subscription = 'optional' - exra_css = """ + exra_css = ''' body{font-family: Arial,Helvetica,sans-serif;} .print-created{font-size: small;} .caption{display: block; font-size: x-small;} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language @@ -47,7 +47,7 @@ class Thenation(BasicNewsRecipe): ] remove_attributes = ['lang'] - feeds = [(u"Articles", u'http://www.thenation.com/rss/articles')] + feeds = [(u'Articles', u'http://www.thenation.com/rss/articles')] def get_browser(self): br = BasicNewsRecipe.get_browser(self) diff --git a/recipes/the_philippine_daily_inquirer.recipe b/recipes/the_philippine_daily_inquirer.recipe index 70a6b84cc5..222414159a 100644 --- a/recipes/the_philippine_daily_inquirer.recipe +++ b/recipes/the_philippine_daily_inquirer.recipe @@ -5,7 +5,7 @@ from calibre.web.feeds.recipes import BasicNewsRecipe class PhilippineDailyInquirer(BasicNewsRecipe): title = 'The Philippine Daily Inquirer' - custom_title = "The Philippine Daily Inquirer - " + \ + custom_title = 'The Philippine Daily Inquirer - ' + \ time.strftime('%d %b %Y %I:%M %p') __author__ = 'jde' __date__ = '03 June 2012' diff --git a/recipes/the_saturday_paper.recipe b/recipes/the_saturday_paper.recipe index 46276e4768..d71e09446f 100644 --- a/recipes/the_saturday_paper.recipe +++ b/recipes/the_saturday_paper.recipe @@ -32,7 +32,7 @@ class SaturdayPaper(BasicNewsRecipe): ' article-page__sidebar article-page__social__icons share-wrapper article-footer-container') ] remove_tags_after = [ - {"name": "div", "class": "end-matter"}, + {'name': 'div', 'class': 'end-matter'}, ] @@ -62,7 +62,7 @@ class SaturdayPaper(BasicNewsRecipe): title = a.find(class_='article__title') title = self.tag_to_string(title) - url = a.find(class_="article__title_link") + url = a.find(class_='article__title_link') if url is None: continue url = url['href'] diff --git a/recipes/the_week_magazine_free.recipe b/recipes/the_week_magazine_free.recipe index 47bc045d2d..35718ae649 100644 --- a/recipes/the_week_magazine_free.recipe +++ b/recipes/the_week_magazine_free.recipe @@ -9,7 +9,7 @@ class TheWeek(BasicNewsRecipe): title = 'The Week' __author__ = 'unkn0wn' description = ( - 'The Week is for readers who want to know what\'s going on in the world, without having to read ' + "The Week is for readers who want to know what's going on in the world, without having to read " 'several daily newspapers or get wrapped up in the endless news cycle. For every important story, ' 'our editors carefully select commentary from all sides of the debate and artfully stitch them together ' 'into one concise read. By showing you every perspective, we enable you to form your own opinion.' diff --git a/recipes/the_week_uk.recipe b/recipes/the_week_uk.recipe index 674d44f88c..32f4ca0495 100644 --- a/recipes/the_week_uk.recipe +++ b/recipes/the_week_uk.recipe @@ -9,7 +9,7 @@ class TheWeek(BasicNewsRecipe): title = 'The Week' __author__ = 'unkn0wn' description = ( - 'The Week is for readers who want to know what\'s going on in the world, without having to read ' + "The Week is for readers who want to know what's going on in the world, without having to read " 'several daily newspapers or get wrapped up in the endless news cycle. For every important story, ' 'our editors carefully select commentary from all sides of the debate and artfully stitch them together ' 'into one concise read. By showing you every perspective, we enable you to form your own opinion.' diff --git a/recipes/theecocolapse.recipe b/recipes/theecocolapse.recipe index 9131883289..d90a340282 100644 --- a/recipes/theecocolapse.recipe +++ b/recipes/theecocolapse.recipe @@ -20,10 +20,10 @@ class TheEconomicCollapse(BasicNewsRecipe): use_embedded_content = False language = 'en' remove_empty_feeds = True - extra_css = """ + extra_css = ''' body{font-family: Tahoma,Arial,sans-serif } img{margin-bottom: 0.4em; display: block;} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language diff --git a/recipes/theeconomictimes_india.recipe b/recipes/theeconomictimes_india.recipe index 4a4762cc90..b824d1258f 100644 --- a/recipes/theeconomictimes_india.recipe +++ b/recipes/theeconomictimes_india.recipe @@ -54,7 +54,7 @@ class TheEconomicTimes(BasicNewsRecipe): def preprocess_html(self, soup): for image in soup.findAll('img', attrs={'src': True}): - image['src'] = image['src'].replace("width-300", "width-640") + image['src'] = image['src'].replace('width-300', 'width-640') for img in soup.findAll('img', attrs={'data-original': True}): img['src'] = img['data-original'].replace('photo', 'thumb').replace('quality-100', 'quality-100,width-600,resizemode-4') return soup diff --git a/recipes/theeconomictimes_india_print_edition.recipe b/recipes/theeconomictimes_india_print_edition.recipe index 6895d870fd..415097ea48 100644 --- a/recipes/theeconomictimes_india_print_edition.recipe +++ b/recipes/theeconomictimes_india_print_edition.recipe @@ -40,7 +40,7 @@ class TheEconomicTimes(BasicNewsRecipe): try: br.open(cover) except: - self.log("\nCover unavailable") + self.log('\nCover unavailable') cover = None return cover @@ -81,7 +81,7 @@ class TheEconomicTimes(BasicNewsRecipe): secname = re.sub(r'[0-9]', '', sec) self.log(secname) articles = [] - for h3 in section.findAll(("h1", "h3", "h4", "h5")): + for h3 in section.findAll(('h1', 'h3', 'h4', 'h5')): span = h3.find( 'span', href=lambda x: x and x.startswith('https://economictimes.indiatimes.com/epaper/'), @@ -102,7 +102,7 @@ class TheEconomicTimes(BasicNewsRecipe): def preprocess_html(self, soup): for image in soup.findAll('img', attrs={'src': True}): - image['src'] = image['src'].replace("width-300", "width-640") + image['src'] = image['src'].replace('width-300', 'width-640') for img in soup.findAll('img', attrs={'data-original': True}): img['src'] = img['data-original'] return soup diff --git a/recipes/thenewcriterion.recipe b/recipes/thenewcriterion.recipe index cb56af5181..732d1ecac7 100644 --- a/recipes/thenewcriterion.recipe +++ b/recipes/thenewcriterion.recipe @@ -45,9 +45,9 @@ class TheNewCriterion(BasicNewsRecipe): fetch_retries = 10 auto_cleanup = True masthead_url = 'https://www.newcriterion.com/themes/thenewcriterion/assets/img/horizontal-logo.svg' - extra_css = """ + extra_css = ''' body{font-family: Galliard, serif} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language @@ -112,6 +112,6 @@ class TheNewCriterion(BasicNewsRecipe): self.temp_files.append(tfile) result = tfile.name except: - print("Retrying download...") + print('Retrying download...') count += 1 return result diff --git a/recipes/theoldie.recipe b/recipes/theoldie.recipe index 449064a5bf..7882e1b31d 100644 --- a/recipes/theoldie.recipe +++ b/recipes/theoldie.recipe @@ -219,31 +219,31 @@ class PrivateEyeRecipe(BasicNewsRecipe): # We remove vast swathes of HTML which is not part of the articles. remove_tags_before = [ - {'name': 'div', 'class': "container"}, - {'name': 'div', 'class': "content-wrapper"}, - {'name': 'div', 'class': "only-in-the-magazine"}, + {'name': 'div', 'class': 'container'}, + {'name': 'div', 'class': 'content-wrapper'}, + {'name': 'div', 'class': 'only-in-the-magazine'}, ] remove_tags_after = [ - {'name': 'div', 'class': "container"}, - {'name': 'div', 'class': "content-wrapper"}, - {'name': 'h2', 'string': "Find out more about The Oldie"}, + {'name': 'div', 'class': 'container'}, + {'name': 'div', 'class': 'content-wrapper'}, + {'name': 'h2', 'string': 'Find out more about The Oldie'}, ] # Remove non-sibling content remove_tags = [ - {'name': 'nav', 'class': "categories"}, - {'name': 'div', 'class': "internal-placeholders"}, - {'name': 'div', 'class': "leaderboard"}, - {'name': 'div', 'class': "share"}, - {'name': 'div', 'class': "most-popular"}, - {'name': 'div', 'class': "article-convert"}, + {'name': 'nav', 'class': 'categories'}, + {'name': 'div', 'class': 'internal-placeholders'}, + {'name': 'div', 'class': 'leaderboard'}, + {'name': 'div', 'class': 'share'}, + {'name': 'div', 'class': 'most-popular'}, + {'name': 'div', 'class': 'article-convert'}, # {'name': 'p', 'class': "article-convert"}, # {'name': 'p', 'class': "meta"}, {'name': 'hr'}, - {'name': 'a', 'class': "view-full-screen"}, - {'name': 'div', 'class': "image-counter"}, - {'name': 'h2', 'string': "Find out more about The Oldie"}, - {'name': 'a', 'href': re.compile(r"^https?:\/\/issuu.com\/")}, - {'name': 'img', 'src': re.compile(r"\/assets\/images\/icons\/icon-")}, + {'name': 'a', 'class': 'view-full-screen'}, + {'name': 'div', 'class': 'image-counter'}, + {'name': 'h2', 'string': 'Find out more about The Oldie'}, + {'name': 'a', 'href': re.compile(r'^https?:\/\/issuu.com\/')}, + {'name': 'img', 'src': re.compile(r'\/assets\/images\/icons\/icon-')}, ] # The following extra css is to tweak the formatting of various elements of various article pages. diff --git a/recipes/tijd.recipe b/recipes/tijd.recipe index 4a0507343b..987d880b1c 100644 --- a/recipes/tijd.recipe +++ b/recipes/tijd.recipe @@ -76,12 +76,12 @@ class DeTijd(BasicNewsRecipe): soup.html['lang'] = self.lang soup.html['dir'] = self.direction mlang = new_tag( - soup, 'meta', [("http-equiv", "Content-Language"), - ("content", self.lang)] + soup, 'meta', [('http-equiv', 'Content-Language'), + ('content', self.lang)] ) mcharset = new_tag( - soup, 'meta', [("http-equiv", "Content-Type"), - ("content", "text/html; charset=utf-8")] + soup, 'meta', [('http-equiv', 'Content-Type'), + ('content', 'text/html; charset=utf-8')] ) soup.head.insert(0, mlang) soup.head.insert(1, mcharset) diff --git a/recipes/toi.recipe b/recipes/toi.recipe index ebe7c52712..023bcf2f8b 100644 --- a/recipes/toi.recipe +++ b/recipes/toi.recipe @@ -29,7 +29,7 @@ class TheEconomicTimes(BasicNewsRecipe): language = 'en_IN' publication_type = 'newspaper' masthead_url = 'http://timesofindia.indiatimes.com/photo.cms?msid=2419189' - extra_css = """ + extra_css = ''' body{font-family: Arial,Helvetica,sans-serif} .foto_mg{font-size: 60%; font-weight: 700;} @@ -37,7 +37,7 @@ class TheEconomicTimes(BasicNewsRecipe): artdate{font-size: 60%} artag{font-size: 60%} div.storycontent{padding-top: 10px} - """ + ''' conversion_options = {'comment': description, 'tags': category, 'publisher': publisher, diff --git a/recipes/toiprint.recipe b/recipes/toiprint.recipe index 3bc662766d..8fa9627f67 100644 --- a/recipes/toiprint.recipe +++ b/recipes/toiprint.recipe @@ -97,7 +97,7 @@ class toiprint(BasicNewsRecipe): else: desc = 'Page No.' + url.split('_')[-3] + ' | ' + art.get('ColumnTitle', '') self.log('\t', title, '\n\t', desc.replace('\n', '')) - feeds_dict[section].append({"title": title, "url": url, "description": desc}) + feeds_dict[section].append({'title': title, 'url': url, 'description': desc}) def sort_key(x): section = x[0] try: diff --git a/recipes/tyzden.recipe b/recipes/tyzden.recipe index 838ddf6965..a2623db7dc 100644 --- a/recipes/tyzden.recipe +++ b/recipes/tyzden.recipe @@ -50,7 +50,7 @@ class Tyzden(BasicNewsRecipe): 'attrs': { 'class': re.compile(r'\barticle\b')}}, ] - extra_css = """.theme-highlight { + extra_css = '''.theme-highlight { color: #999; text-decoration: none; } @@ -90,7 +90,7 @@ class Tyzden(BasicNewsRecipe): .teaser__title .highlight { color: #000; } - """ + ''' def get_browser(self): br = BasicNewsRecipe.get_browser(self) diff --git a/recipes/ugeskriftet.recipe b/recipes/ugeskriftet.recipe index d91e5bf0c6..6a58804098 100644 --- a/recipes/ugeskriftet.recipe +++ b/recipes/ugeskriftet.recipe @@ -23,13 +23,13 @@ class Ugeskriftet(BasicNewsRecipe): ]}) ] remove_tags = [dict(name='img')] - extra_css = """ + extra_css = ''' h1{font-weight: bold; font-size: large;} b{font-weight: bold; font-size: medium;} h2{font-weight: bold; font-size: large;} h3{font-weight: bold; font-size: large;} h4{font-weight: bold; font-size: large;} - """ + ''' feeds = [ ('Ugeskriftet for læger', 'https://ugeskriftet.dk/rss/forside'), diff --git a/recipes/uncrate.recipe b/recipes/uncrate.recipe index 86056a2ec8..549d5f6af3 100644 --- a/recipes/uncrate.recipe +++ b/recipes/uncrate.recipe @@ -55,9 +55,9 @@ class Uncrate(BasicNewsRecipe): def preprocess_html(self, soup): mlang = new_tag(soup, 'meta', [ - ("http-equiv", "Content-Language"), ("content", self.lang)]) + ('http-equiv', 'Content-Language'), ('content', self.lang)]) mcharset = new_tag(soup, 'meta', [ - ("http-equiv", "Content-Type"), ("content", "text/html; charset=utf-8")]) + ('http-equiv', 'Content-Type'), ('content', 'text/html; charset=utf-8')]) soup.head.insert(0, mlang) soup.head.insert(1, mcharset) for item in soup.findAll(style=True): diff --git a/recipes/unian_net_en.recipe b/recipes/unian_net_en.recipe index 55d9a43f64..9fe8869f19 100644 --- a/recipes/unian_net_en.recipe +++ b/recipes/unian_net_en.recipe @@ -7,7 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe class Unian(BasicNewsRecipe): title = 'UNIAN' description = ('UNIAN (Ukrainian Independent News Agency of News) is the largest independent news agency,' - ' first in Ukraine, founded in 1993, remaining the leader among the country\'s news media,' + " first in Ukraine, founded in 1993, remaining the leader among the country's news media," ' being the most cited source of news from across Ukraine.') __author__ = 'bugmen00t' publication_type = 'newspaper' diff --git a/recipes/vancouver_province.recipe b/recipes/vancouver_province.recipe index 838be31c73..f6281b62e6 100644 --- a/recipes/vancouver_province.recipe +++ b/recipes/vancouver_province.recipe @@ -164,7 +164,7 @@ class CanWestPaper(BasicNewsRecipe): continue break if daysback == 7: - self.log("\nCover unavailable") + self.log('\nCover unavailable') cover = None return cover @@ -183,18 +183,18 @@ class CanWestPaper(BasicNewsRecipe): def fixChars(self, string): # Replace lsquo (\x91) - fixed = re.sub("\x91", "‘", string) + fixed = re.sub('\x91', '‘', string) # Replace rsquo (\x92) - fixed = re.sub("\x92", "’", fixed) + fixed = re.sub('\x92', '’', fixed) # Replace ldquo (\x93) - fixed = re.sub("\x93", "“", fixed) + fixed = re.sub('\x93', '“', fixed) # Replace rdquo (\x94) - fixed = re.sub("\x94", "”", fixed) + fixed = re.sub('\x94', '”', fixed) # Replace ndash (\x96) - fixed = re.sub("\x96", "–", fixed) + fixed = re.sub('\x96', '–', fixed) # Replace mdash (\x97) - fixed = re.sub("\x97", "—", fixed) - fixed = re.sub("’", "’", fixed) + fixed = re.sub('\x97', '—', fixed) + fixed = re.sub('’', '’', fixed) return fixed def massageNCXText(self, description): @@ -275,10 +275,10 @@ class CanWestPaper(BasicNewsRecipe): if url.startswith('/'): url = self.url_prefix + url if not url.startswith(self.url_prefix): - print("Rejected " + url) + print('Rejected ' + url) return if url in self.url_list: - print("Rejected dup " + url) + print('Rejected dup ' + url) return self.url_list.append(url) title = self.tag_to_string(atag, False) @@ -290,8 +290,8 @@ class CanWestPaper(BasicNewsRecipe): return dtag = adiv.find('div', 'content') description = '' - print("URL " + url) - print("TITLE " + title) + print('URL ' + url) + print('TITLE ' + title) if dtag is not None: stag = dtag.span if stag is not None: @@ -299,18 +299,18 @@ class CanWestPaper(BasicNewsRecipe): description = self.tag_to_string(stag, False) else: description = self.tag_to_string(dtag, False) - print("DESCRIPTION: " + description) + print('DESCRIPTION: ' + description) if key not in articles: articles[key] = [] articles[key].append(dict( title=title, url=url, date='', description=description, author='', content='')) def parse_web_index(key, keyurl): - print("Section: " + key + ': ' + self.url_prefix + keyurl) + print('Section: ' + key + ': ' + self.url_prefix + keyurl) try: soup = self.index_to_soup(self.url_prefix + keyurl) except: - print("Section: " + key + ' NOT FOUND') + print('Section: ' + key + ' NOT FOUND') return ans.append(key) mainsoup = soup.find('div', 'bodywrapper') diff --git a/recipes/vancouver_sun.recipe b/recipes/vancouver_sun.recipe index 4c84e58c0f..5326e44731 100644 --- a/recipes/vancouver_sun.recipe +++ b/recipes/vancouver_sun.recipe @@ -165,24 +165,24 @@ class CanWestPaper(BasicNewsRecipe): continue break if daysback == 7: - self.log("\nCover unavailable") + self.log('\nCover unavailable') cover = None return cover def fixChars(self, string): # Replace lsquo (\x91) - fixed = re.sub("\x91", "‘", string) + fixed = re.sub('\x91', '‘', string) # Replace rsquo (\x92) - fixed = re.sub("\x92", "’", fixed) + fixed = re.sub('\x92', '’', fixed) # Replace ldquo (\x93) - fixed = re.sub("\x93", "“", fixed) + fixed = re.sub('\x93', '“', fixed) # Replace rdquo (\x94) - fixed = re.sub("\x94", "”", fixed) + fixed = re.sub('\x94', '”', fixed) # Replace ndash (\x96) - fixed = re.sub("\x96", "–", fixed) + fixed = re.sub('\x96', '–', fixed) # Replace mdash (\x97) - fixed = re.sub("\x97", "—", fixed) - fixed = re.sub("’", "’", fixed) + fixed = re.sub('\x97', '—', fixed) + fixed = re.sub('’', '’', fixed) return fixed def massageNCXText(self, description): @@ -263,10 +263,10 @@ class CanWestPaper(BasicNewsRecipe): if url.startswith('/'): url = self.url_prefix + url if not url.startswith(self.url_prefix): - print("Rejected " + url) + print('Rejected ' + url) return if url in self.url_list: - print("Rejected dup " + url) + print('Rejected dup ' + url) return self.url_list.append(url) title = self.tag_to_string(atag, False) @@ -278,8 +278,8 @@ class CanWestPaper(BasicNewsRecipe): return dtag = adiv.find('div', 'content') description = '' - print("URL " + url) - print("TITLE " + title) + print('URL ' + url) + print('TITLE ' + title) if dtag is not None: stag = dtag.span if stag is not None: @@ -287,18 +287,18 @@ class CanWestPaper(BasicNewsRecipe): description = self.tag_to_string(stag, False) else: description = self.tag_to_string(dtag, False) - print("DESCRIPTION: " + description) + print('DESCRIPTION: ' + description) if key not in articles: articles[key] = [] articles[key].append(dict( title=title, url=url, date='', description=description, author='', content='')) def parse_web_index(key, keyurl): - print("Section: " + key + ': ' + self.url_prefix + keyurl) + print('Section: ' + key + ': ' + self.url_prefix + keyurl) try: soup = self.index_to_soup(self.url_prefix + keyurl) except: - print("Section: " + key + ' NOT FOUND') + print('Section: ' + key + ' NOT FOUND') return ans.append(key) mainsoup = soup.find('div', 'bodywrapper') diff --git a/recipes/variety.recipe b/recipes/variety.recipe index 26a1a12b1c..c74bd5d8f5 100644 --- a/recipes/variety.recipe +++ b/recipes/variety.recipe @@ -20,13 +20,13 @@ class Variety(BasicNewsRecipe): category = 'Entertainment Industry News, Daily Variety, Movie Reviews, TV, Awards, Oscars, Cannes, Box Office, Hollywood' language = 'en' masthead_url = 'http://images1.variety.com/graphics/variety/Variety_logo_green_tm.gif' - extra_css = """ + extra_css = ''' body{font-family: Arial,Helvetica,sans-serif; font-size: 1.275em} .date{font-size: small; border: 1px dotted rgb(204, 204, 204); font-style: italic; color: rgb(102, 102, 102); margin: 5px 0px; padding: 0.5em;} .author{margin: 5px 0px 5px 20px; padding: 0.5em; background: none repeat scroll 0% 0% rgb(247, 247, 247);} .art h2{color: rgb(153, 0, 0); font-size: 1.275em; font-weight: bold;} img{margin-bottom: 1em} - """ + ''' conversion_options = { 'comments': description, 'tags': category, 'language': language, 'publisher': publisher diff --git a/recipes/vecernji_list.recipe b/recipes/vecernji_list.recipe index 5c74db613c..5bcd540f18 100644 --- a/recipes/vecernji_list.recipe +++ b/recipes/vecernji_list.recipe @@ -23,7 +23,7 @@ def new_tag(soup, name, attrs=()): class VecernjiList(BasicNewsRecipe): title = 'Vecernji List' __author__ = 'Darko Miletic' - description = "Vecernji.hr je vodeci hrvatski news portal. Cilj je biti prvi u objavljivanju svih vijesti iz Hrvatske, svijeta, sporta, gospodarstva, showbiza i jos mnogo vise. Uz cjelodnevni rad, novinari objavljuju preko 300 raznih vijesti svakoga dana. Vecernji.hr prati sve vaznije dogadaje specijalnim izvjestajima, video specijalima i foto galerijama." # noqa: E501 + description = 'Vecernji.hr je vodeci hrvatski news portal. Cilj je biti prvi u objavljivanju svih vijesti iz Hrvatske, svijeta, sporta, gospodarstva, showbiza i jos mnogo vise. Uz cjelodnevni rad, novinari objavljuju preko 300 raznih vijesti svakoga dana. Vecernji.hr prati sve vaznije dogadaje specijalnim izvjestajima, video specijalima i foto galerijama.' # noqa: E501 publisher = 'Vecernji.hr' category = 'news, politics, Croatia' oldest_article = 2 @@ -57,9 +57,9 @@ class VecernjiList(BasicNewsRecipe): soup.html['dir'] = self.direction mlang = new_tag(soup, 'meta', [ - ("http-equiv", "Content-Language"), ("content", self.lang)]) + ('http-equiv', 'Content-Language'), ('content', self.lang)]) mcharset = new_tag(soup, 'meta', [ - ("http-equiv", "Content-Type"), ("content", "text/html; charset=UTF-8")]) + ('http-equiv', 'Content-Type'), ('content', 'text/html; charset=UTF-8')]) soup.head.insert(0, mlang) soup.head.insert(1, mcharset) return self.adeify_images(soup) diff --git a/recipes/vic_times.recipe b/recipes/vic_times.recipe index 3e11e31e39..034724e7b6 100644 --- a/recipes/vic_times.recipe +++ b/recipes/vic_times.recipe @@ -100,7 +100,7 @@ class TimesColonist(BasicNewsRecipe): dict(name='div', attrs={ 'class': re.compile('window')}), dict(name='div', attrs={'class': re.compile('related.news.element')})] - print("PROFILE NAME = " + options.output_profile.short_name) + print('PROFILE NAME = ' + options.output_profile.short_name) if self.kindle_omit_images and options.output_profile.short_name in ['kindle', 'kindle_dx', 'kindle_pw']: self.remove_tags.append( dict(name='div', attrs={'class': re.compile('image-container')})) @@ -127,24 +127,24 @@ class TimesColonist(BasicNewsRecipe): continue break if daysback == 7: - self.log("\nCover unavailable") + self.log('\nCover unavailable') cover = None return cover def fixChars(self, string): # Replace lsquo (\x91) - fixed = re.sub("\x91", "‘", string) + fixed = re.sub('\x91', '‘', string) # Replace rsquo (\x92) - fixed = re.sub("\x92", "’", fixed) + fixed = re.sub('\x92', '’', fixed) # Replace ldquo (\x93) - fixed = re.sub("\x93", "“", fixed) + fixed = re.sub('\x93', '“', fixed) # Replace rdquo (\x94) - fixed = re.sub("\x94", "”", fixed) + fixed = re.sub('\x94', '”', fixed) # Replace ndash (\x96) - fixed = re.sub("\x96", "–", fixed) + fixed = re.sub('\x96', '–', fixed) # Replace mdash (\x97) - fixed = re.sub("\x97", "—", fixed) - fixed = re.sub("’", "’", fixed) + fixed = re.sub('\x97', '—', fixed) + fixed = re.sub('’', '’', fixed) return fixed def massageNCXText(self, description): @@ -229,11 +229,11 @@ class TimesColonist(BasicNewsRecipe): description = self.tag_to_string(dtag, False) article_list.append(dict( title=title, url=url, date='', description=description, author='', content='')) - print(sectitle + title + ": description = " + - description + " URL=" + url + '\n\r') + print(sectitle + title + ': description = ' + + description + ' URL=' + url + '\n\r') def add_section_index(self, ans, securl, sectitle): - print("Add section url=" + self.url_prefix + '/' + securl + '\n\r') + print('Add section url=' + self.url_prefix + '/' + securl + '\n\r') try: soup = self.index_to_soup(self.url_prefix + '/' + securl) except: diff --git a/recipes/villagevoice.recipe b/recipes/villagevoice.recipe index 7f300a94e9..69a9ab1809 100644 --- a/recipes/villagevoice.recipe +++ b/recipes/villagevoice.recipe @@ -9,10 +9,10 @@ class VillageVoice(BasicNewsRecipe): title = 'Village Voice' feeds = [ - ("Complete Issue", "http://villagevoice.com/syndication/issue"), - ("News", "http://villagevoice.com/syndication/section/news"), - ("Music", "http://villagevoice.com/syndication/section/music"), - ("Movies", "http://villagevoice.com/syndication/section/film"), + ('Complete Issue', 'http://villagevoice.com/syndication/issue'), + ('News', 'http://villagevoice.com/syndication/section/news'), + ('Music', 'http://villagevoice.com/syndication/section/music'), + ('Movies', 'http://villagevoice.com/syndication/section/film'), # ("Restaurants", "http://villagevoice.com/syndication/section/dining"), # ("Music Events", "http://villagevoice.com/syndication/events?type=music"), # ("Calendar Events", "http://villagevoice.com/syndication/events"), @@ -22,7 +22,7 @@ class VillageVoice(BasicNewsRecipe): auto_cleanup = True max_articles_per_feed = 50 - masthead_url = "http://assets.villagevoice.com/img/citylogo.png" + masthead_url = 'http://assets.villagevoice.com/img/citylogo.png' language = 'en' __author__ = 'Barty' diff --git a/recipes/volksrant.recipe b/recipes/volksrant.recipe index 6fe050a769..c6cd3d6779 100644 --- a/recipes/volksrant.recipe +++ b/recipes/volksrant.recipe @@ -34,7 +34,7 @@ class Volkskrant(BasicNewsRecipe): dict(attrs={'data-element-id': ['article-element-authors']}), dict(name=['script', 'noscript', 'style']), ] - remove_attributes = ["class", "id", "name", "style"] + remove_attributes = ['class', 'id', 'name', 'style'] encoding = 'utf-8' no_stylesheets = True ignore_duplicate_articles = {'url'} @@ -69,7 +69,7 @@ class Volkskrant(BasicNewsRecipe): 'span', attrs={'class': 'teaser__title__value--short'} ) ).strip() - if teaser_label.lower() == "podcast": + if teaser_label.lower() == 'podcast': continue parts = [] if teaser_label: @@ -101,10 +101,10 @@ class Volkskrant(BasicNewsRecipe): tag['src'] = 'https://www.volkskrant.nl' + tag['src'] for tag in soup(): - if tag.name == "picture": - tag.replaceWith(tag.find("img")) + if tag.name == 'picture': + tag.replaceWith(tag.find('img')) - comic_articles = { "Bas van der Schot", "Poldermodellen", "Gummbah", "Sigmund" } + comic_articles = { 'Bas van der Schot', 'Poldermodellen', 'Gummbah', 'Sigmund' } if self.tag_to_string(soup.find('h1')).strip() in comic_articles: for node in soup.find('figure').find_next_siblings(): node.extract() @@ -116,8 +116,8 @@ class Volkskrant(BasicNewsRecipe): 'Accept': 'application/json, text/javascript, */*; q=0.01', 'DNT': '1', } - url = "https://login-api.e-pages.dk/v1/krant.volkskrant.nl/folders" + url = 'https://login-api.e-pages.dk/v1/krant.volkskrant.nl/folders' with closing(self.browser.open(Request(url, None, headers))) as r: folders = json.loads(r.read()) - return folders["objects"][0]["teaser_medium"] + return folders['objects'][0]['teaser_medium'] return None diff --git a/recipes/vreme.recipe b/recipes/vreme.recipe index f8b88e67eb..fc83944a97 100644 --- a/recipes/vreme.recipe +++ b/recipes/vreme.recipe @@ -26,7 +26,7 @@ class Vreme(BasicNewsRecipe): language = 'sr' publication_type = 'magazine' masthead_url = 'http://www.vreme.com/g/vreme-logo.gif' - extra_css = """ + extra_css = ''' @font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: serif1, serif} @@ -35,7 +35,7 @@ class Vreme(BasicNewsRecipe): .column-heading1{font-family: sans1, sans-serif; font-size: x-large} .column-normal{font-family: sans1, sans-serif; font-size: medium} .large{font-family: sans1, sans-serif; font-size: large} - """ + ''' conversion_options = { 'comment': description, 'tags': category, 'publisher': publisher, 'language': language, 'linearize_tables': True diff --git a/recipes/windows_star.recipe b/recipes/windows_star.recipe index 07158419d8..89fbc5347c 100644 --- a/recipes/windows_star.recipe +++ b/recipes/windows_star.recipe @@ -69,14 +69,14 @@ class CanWestPaper(BasicNewsRecipe): ans = ['News'] # Find each instance of class="sectiontitle", class="featurecontent" - for divtag in soup.findAll('div', attrs={'class': ["section_title02", "featurecontent"]}): + for divtag in soup.findAll('div', attrs={'class': ['section_title02', 'featurecontent']}): if 'section_title' in ''.join(divtag['class']): # div contains section title if not divtag.h3: continue key = self.tag_to_string(divtag.h3, False) ans.append(key) - self.log("Section name %s" % key) + self.log('Section name %s' % key) continue # div contains article data h1tag = divtag.find('h1') diff --git a/recipes/windsor_star.recipe b/recipes/windsor_star.recipe index 763f8cfa4f..c28eebe4f6 100644 --- a/recipes/windsor_star.recipe +++ b/recipes/windsor_star.recipe @@ -123,24 +123,24 @@ class CanWestPaper(BasicNewsRecipe): continue break if daysback == 7: - self.log("\nCover unavailable") + self.log('\nCover unavailable') cover = None return cover def fixChars(self, string): # Replace lsquo (\x91) - fixed = re.sub("\x91", "‘", string) + fixed = re.sub('\x91', '‘', string) # Replace rsquo (\x92) - fixed = re.sub("\x92", "’", fixed) + fixed = re.sub('\x92', '’', fixed) # Replace ldquo (\x93) - fixed = re.sub("\x93", "“", fixed) + fixed = re.sub('\x93', '“', fixed) # Replace rdquo (\x94) - fixed = re.sub("\x94", "”", fixed) + fixed = re.sub('\x94', '”', fixed) # Replace ndash (\x96) - fixed = re.sub("\x96", "–", fixed) + fixed = re.sub('\x96', '–', fixed) # Replace mdash (\x97) - fixed = re.sub("\x97", "—", fixed) - fixed = re.sub("’", "’", fixed) + fixed = re.sub('\x97', '—', fixed) + fixed = re.sub('’', '’', fixed) return fixed def massageNCXText(self, description): @@ -180,7 +180,7 @@ class CanWestPaper(BasicNewsRecipe): ans = ['News'] # Find each instance of class="sectiontitle", class="featurecontent" - for divtag in soup.findAll('div', attrs={'class': ["section_title02", "featurecontent"]}): + for divtag in soup.findAll('div', attrs={'class': ['section_title02', 'featurecontent']}): # self.log(" div class = %s" % divtag['class']) if ''.join(divtag['class']).startswith('section_title'): # div contains section title @@ -188,7 +188,7 @@ class CanWestPaper(BasicNewsRecipe): continue key = self.tag_to_string(divtag.h3, False) ans.append(key) - self.log("Section name %s" % key) + self.log('Section name %s' % key) continue # div contains article data h1tag = divtag.find('h1') diff --git a/recipes/wired.recipe b/recipes/wired.recipe index cc1d5d021a..86e64b7929 100644 --- a/recipes/wired.recipe +++ b/recipes/wired.recipe @@ -32,14 +32,14 @@ class WiredDailyNews(BasicNewsRecipe): language = 'en' ignore_duplicate_articles = {'url'} remove_empty_feeds = True - extra_css = """ + extra_css = ''' .entry-header{ text-transform: uppercase; vertical-align: baseline; display: inline; } ul li{display: inline} - """ + ''' keep_only_tags = [ classes('content-header lead-asset article__body'), @@ -57,7 +57,7 @@ class WiredDailyNews(BasicNewsRecipe): self.log('Parsing index page', currenturl) soup = self.index_to_soup(currenturl) baseurl = 'https://www.wired.com' - for a in soup.find("div", {"class" : 'multi-packages'}).findAll('a', href=True): + for a in soup.find('div', {'class' : 'multi-packages'}).findAll('a', href=True): url = a['href'] if url.startswith('/story') and url.endswith('/'): title = self.tag_to_string(a.parent.find('h3')) diff --git a/recipes/wired_daily.recipe b/recipes/wired_daily.recipe index c8e1918423..f73c682219 100644 --- a/recipes/wired_daily.recipe +++ b/recipes/wired_daily.recipe @@ -37,14 +37,14 @@ class WiredDailyNews(BasicNewsRecipe): ignore_duplicate_articles = {'url'} remove_empty_feeds = True publication_type = 'newsportal' - extra_css = """ + extra_css = ''' .entry-header{ text-transform: uppercase; vertical-align: baseline; display: inline; } ul li{display: inline} - """ + ''' recipe_specific_options = { 'days': { diff --git a/recipes/words_without_borders.recipe b/recipes/words_without_borders.recipe index 136ed8f14c..cc3a9b7c74 100644 --- a/recipes/words_without_borders.recipe +++ b/recipes/words_without_borders.recipe @@ -19,7 +19,7 @@ class AdvancedUserRecipe1308302002(BasicNewsRecipe): remove_tags_after = [ {'class': 'addthis_toolbox addthis_default_style no_print'}] remove_tags = [{'class': ['posterous_quote_citation', 'button']}] - extra_css = """ - h1{font-family: Georgia,serif; font-size: large}h2{font-family: Georgia,serif; font-size: large} """ + extra_css = ''' + h1{font-family: Georgia,serif; font-size: large}h2{font-family: Georgia,serif; font-size: large} ''' feeds = [(u'wwb', u'http://feeds.feedburner.com/wwborders?format=xml')] diff --git a/recipes/wsj.recipe b/recipes/wsj.recipe index a588271ce0..076925551c 100644 --- a/recipes/wsj.recipe +++ b/recipes/wsj.recipe @@ -14,7 +14,7 @@ class WSJ(BasicNewsRecipe): __author__ = 'unkn0wn' description = ( 'The Print Edition of WSJ. The Wall Street Journal is your source for breaking news, analysis and insights from the U.S. and ' - 'around the world, the world\'s leading business and finance publication.' + "around the world, the world's leading business and finance publication." ) language = 'en_US' masthead_url = 'https://s.wsj.net/media/wsj_amp_masthead_lg.png' diff --git a/recipes/wsj_free.recipe b/recipes/wsj_free.recipe index d818c5ec7b..7ba539a6d9 100644 --- a/recipes/wsj_free.recipe +++ b/recipes/wsj_free.recipe @@ -180,7 +180,7 @@ class WSJ(BasicNewsRecipe): # you can get the version below from lib-min.js # search for: "\d+\.\d+\.\d+" # This might need to be updated in the future - auth0_client = json.dumps({"name": "auth0.js-ulp", "version": "9.11.3"}) + auth0_client = json.dumps({'name': 'auth0.js-ulp', 'version': '9.11.3'}) if not isinstance(auth0_client, bytes): auth0_client = auth0_client.encode('utf-8') auth0_client = standard_b64encode(auth0_client) @@ -260,7 +260,7 @@ class WSJ(BasicNewsRecipe): articles.append({'title': title, 'url': url, 'description': desc, 'date': ''}) self.log('\tFound article:', title) - self.log('\t\t', desc + " " + url) + self.log('\t\t', desc + ' ' + url) if self.test and len(articles) >= self.test[1]: break diff --git a/recipes/wsj_news.recipe b/recipes/wsj_news.recipe index 4d0c53a9fb..47777dcb7e 100644 --- a/recipes/wsj_news.recipe +++ b/recipes/wsj_news.recipe @@ -14,7 +14,7 @@ class WSJ(BasicNewsRecipe): __author__ = 'unkn0wn' description = ( 'The Wall Street Journal is your source for breaking news, analysis and insights from the U.S. and ' - 'around the world, the world\'s leading business and finance publication. Get the Latest News here.' + "around the world, the world's leading business and finance publication. Get the Latest News here." ) language = 'en_US' masthead_url = 'https://s.wsj.net/media/wsj_amp_masthead_lg.png' diff --git a/recipes/yomiuri_world.recipe b/recipes/yomiuri_world.recipe index f7babc563a..144c77f1e5 100644 --- a/recipes/yomiuri_world.recipe +++ b/recipes/yomiuri_world.recipe @@ -21,9 +21,9 @@ class YOLNews(BasicNewsRecipe): encoding = 'UTF-8' index = 'http://www.yomiuri.co.jp/world/' remove_javascript = True - masthead_title = u"YOMIURI ONLINE" + masthead_title = u'YOMIURI ONLINE' - keep_only_tags = [{'class': "article text-resizeable"}] + keep_only_tags = [{'class': 'article text-resizeable'}] def parse_feeds(self): feeds = BasicNewsRecipe.parse_feeds(self) diff --git a/recipes/zaobao.recipe b/recipes/zaobao.recipe index 5618a99fd8..83f1c855ca 100644 --- a/recipes/zaobao.recipe +++ b/recipes/zaobao.recipe @@ -172,7 +172,7 @@ class ZAOBAO(BasicNewsRecipe): article.text_summary = article.text_summary.encode( 'cp1252', 'replace').decode(self.encoding, 'replace') - if article.title == "Untitled article": + if article.title == 'Untitled article': self.log(_('Removing empty article %s from %s') % (article.title, article.url)) # remove the article diff --git a/recipes/zdnet.fr.recipe b/recipes/zdnet.fr.recipe index 585f040344..00c83323e4 100644 --- a/recipes/zdnet.fr.recipe +++ b/recipes/zdnet.fr.recipe @@ -62,6 +62,6 @@ class zdnet(BasicNewsRecipe): try: br.open(masthead) except: - self.log("\nCover unavailable") + self.log('\nCover unavailable') masthead = None return masthead diff --git a/recipes/zeitde_sub.recipe b/recipes/zeitde_sub.recipe index 5ec52cdc9d..a255354257 100644 --- a/recipes/zeitde_sub.recipe +++ b/recipes/zeitde_sub.recipe @@ -7,9 +7,9 @@ __copyright__ = '2010, Steffen Siebert ' __docformat__ = 'restructuredtext de' __version__ = '1.5' -""" +''' Die Zeit EPUB -""" +''' import io import os @@ -184,7 +184,7 @@ class ZeitEPUBAbo(BasicNewsRecipe): ] def build_index(self): - url = "https://meine.zeit.de/anmelden?url=https%3A//epaper.zeit.de/abo/diezeit" + url = 'https://meine.zeit.de/anmelden?url=https%3A//epaper.zeit.de/abo/diezeit' browser = self.get_browser() # new login process @@ -241,7 +241,7 @@ class ZeitEPUBAbo(BasicNewsRecipe): self.report_progress( 0, _('trying to download cover image (titlepage)')) self.download_cover() - self.conversion_options["cover"] = self.cover_path + self.conversion_options['cover'] = self.cover_path return index @@ -250,7 +250,7 @@ class ZeitEPUBAbo(BasicNewsRecipe): self.log.warning('Downloading cover') try: self.log.warning('Trying PDF-based cover') - url = "https://meine.zeit.de/anmelden?url=https%3A//epaper.zeit.de/abo/diezeit" + url = 'https://meine.zeit.de/anmelden?url=https%3A//epaper.zeit.de/abo/diezeit' browser = self.get_browser() # new login process diff --git a/recipes/zerodeux.recipe b/recipes/zerodeux.recipe index 1fc7dda613..45e25cbe8d 100644 --- a/recipes/zerodeux.recipe +++ b/recipes/zerodeux.recipe @@ -10,7 +10,7 @@ from calibre.web.feeds.news import BasicNewsRecipe class ZeroDeuxRecipe(BasicNewsRecipe): title = 'Zérodeux' __author__ = 'Kabonix' - description = 'Revue d\'art contemporain trimestrielle' + description = "Revue d'art contemporain trimestrielle" publisher = 'Zérodeux' category = 'art, contemporary art, criticism' language = 'fr' diff --git a/recipes/zycie_warszawy.recipe b/recipes/zycie_warszawy.recipe index 60052d9827..07b7e1c85e 100644 --- a/recipes/zycie_warszawy.recipe +++ b/recipes/zycie_warszawy.recipe @@ -47,5 +47,5 @@ class zyciewarszawy(BasicNewsRecipe): preprocess_regexps = [(re.compile(r',3.jpg'), lambda m: ',2.jpg')] def print_version(self, url): - url += "?print=tak" + url += '?print=tak' return url diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py index b25f68f19a..546916e809 100644 --- a/resources/default_tweaks.py +++ b/resources/default_tweaks.py @@ -427,7 +427,7 @@ maximum_cover_size = (1650, 2200) # control where it is sent. Valid values are "main", "carda", "cardb". Note # that if there isn't enough free space available on the location you choose, # the files will be sent to the location with the most free space. -send_news_to_device_location = "main" +send_news_to_device_location = 'main' #: Unified toolbar on macOS # If you enable this option and restart calibre, the toolbar will be 'unified' diff --git a/ruff-strict-pep8.toml b/ruff-strict-pep8.toml index 9155df0968..986c49a613 100644 --- a/ruff-strict-pep8.toml +++ b/ruff-strict-pep8.toml @@ -18,7 +18,7 @@ quote-style = 'single' [lint] ignore = ['E402', 'E722', 'E741'] -select = ['E', 'F', 'I', 'W', 'INT'] +select = ['E', 'F', 'I', 'W', 'INT', 'Q'] [lint.per-file-ignores] "src/calibre/ebooks/unihandecode/*codepoints.py" = ['E501'] @@ -36,3 +36,9 @@ section-order = ['__python__', "future", "standard-library", "third-party", "fir [lint.isort.sections] '__python__' = ['__python__'] + +[lint.flake8-quotes] +avoid-escape = true +docstring-quotes = 'single' +inline-quotes = 'single' +multiline-quotes = 'single' diff --git a/setup/build.py b/setup/build.py index 3e04d8ae5a..d5599db17c 100644 --- a/setup/build.py +++ b/setup/build.py @@ -61,7 +61,7 @@ class Extension: self.headers = d['headers'] = absolutize(kwargs.get('headers', [])) self.sip_files = d['sip_files'] = absolutize(kwargs.get('sip_files', [])) self.needs_exceptions = d['needs_exceptions'] = kwargs.get('needs_exceptions', False) - self.qt_modules = d['qt_modules'] = kwargs.get('qt_modules', ["widgets"]) + self.qt_modules = d['qt_modules'] = kwargs.get('qt_modules', ['widgets']) self.inc_dirs = d['inc_dirs'] = absolutize(kwargs.get('inc_dirs', [])) self.lib_dirs = d['lib_dirs'] = absolutize(kwargs.get('lib_dirs', [])) self.extra_objs = d['extra_objs'] = absolutize(kwargs.get('extra_objs', [])) @@ -121,7 +121,7 @@ def is_ext_allowed(cross_compile_for: str, ext: Extension) -> bool: if islinux and only == cross_compile_for: return True only = set(only.split()) - q = set(filter(lambda x: globals()["is" + x], ["bsd", "freebsd", "haiku", "linux", "macos", "windows"])) + q = set(filter(lambda x: globals()['is' + x], ['bsd', 'freebsd', 'haiku', 'linux', 'macos', 'windows'])) return len(q.intersection(only)) > 0 return True @@ -541,8 +541,8 @@ class Build(Command): '-DCALIBRE_MODINIT_FUNC=' '{} __attribute__ ((visibility ("default"))) {}'.format(extern_decl, return_type)] if ext.needs_cxx and ext.needs_cxx_std: - if env.cc_output_flag.startswith('/') and ext.needs_cxx == "11": - ext.needs_cxx = "14" + if env.cc_output_flag.startswith('/') and ext.needs_cxx == '11': + ext.needs_cxx = '14' cflags.append(env.std_prefix + 'c++' + ext.needs_cxx_std) if ext.needs_c_std and not env.std_prefix.startswith('/'): @@ -618,7 +618,7 @@ class Build(Command): subprocess.check_call(*args, **kwargs) except: cmdline = ' '.join(['"%s"' % (arg) if ' ' in arg else arg for arg in args[0]]) - print("Error while executing: %s\n" % (cmdline)) + print('Error while executing: %s\n' % (cmdline)) raise def build_headless(self): diff --git a/setup/hosting.py b/setup/hosting.py index 6d54cf67dc..2f270ad111 100644 --- a/setup/hosting.py +++ b/setup/hosting.py @@ -224,7 +224,7 @@ class GitHub(Base): # {{{ def fail(self, r, msg): print(msg, ' Status Code: %s' % r.status_code, file=sys.stderr) - print("JSON from response:", file=sys.stderr) + print('JSON from response:', file=sys.stderr) pprint(dict(r.json()), stream=sys.stderr) raise SystemExit(1) diff --git a/setup/install.py b/setup/install.py index 38930b5595..0d6c8d48e3 100644 --- a/setup/install.py +++ b/setup/install.py @@ -68,7 +68,7 @@ class Develop(Command): dest='fatal_errors', help='If set die on post install errors.') parser.add_option('--no-postinstall', action='store_false', dest='postinstall', default=True, - help='Don\'t run post install actions like creating MAN pages, setting'+ + help="Don't run post install actions like creating MAN pages, setting"+ ' up desktop integration and so on') def add_options(self, parser): diff --git a/setup/installers.py b/setup/installers.py index 2a2de8809d..a45f689701 100644 --- a/setup/installers.py +++ b/setup/installers.py @@ -253,7 +253,7 @@ class ExtDev(Command): ext_dir = build_only(which, '', ext) src = os.path.join(ext_dir, f'{ext}.so') print( - "\n\n\x1b[33;1mWARNING: This does not work on macOS, unless you use un-signed builds with ", + '\n\n\x1b[33;1mWARNING: This does not work on macOS, unless you use un-signed builds with ', ' ./update-on-ox develop\x1b[m', file=sys.stderr, end='\n\n\n') host = 'ox' diff --git a/setup/plugins_mirror.py b/setup/plugins_mirror.py index 325b4d7dea..9c6e8adb48 100644 --- a/setup/plugins_mirror.py +++ b/setup/plugins_mirror.py @@ -62,7 +62,7 @@ socket.setdefaulttimeout(30) def read(url, get_info=False): # {{{ - if url.startswith("file://"): + if url.startswith('file://'): return urlopen(url).read() opener = build_opener() opener.addheaders = [ diff --git a/setup/publish.py b/setup/publish.py index 968860a8f5..1a8cccf29d 100644 --- a/setup/publish.py +++ b/setup/publish.py @@ -237,13 +237,13 @@ class Manual(Command): from polyglot.http_server import HTTPServer, SimpleHTTPRequestHandler HandlerClass = SimpleHTTPRequestHandler ServerClass = HTTPServer - Protocol = "HTTP/1.0" + Protocol = 'HTTP/1.0' server_address = ('127.0.0.1', 8000) HandlerClass.protocol_version = Protocol httpd = ServerClass(server_address, HandlerClass) - print("Serving User Manual on localhost:8000") + print('Serving User Manual on localhost:8000') from calibre.gui2 import open_url open_url('http://localhost:8000') httpd.serve_forever() diff --git a/setup/upload.py b/setup/upload.py index 7c0076d339..dbb5c56209 100644 --- a/setup/upload.py +++ b/setup/upload.py @@ -28,8 +28,8 @@ if __name__ == '__main__': from setup import Command, __appname__, __version__, installer_names DOWNLOADS = '/srv/main/downloads' -HTML2LRF = "calibre/ebooks/lrf/html/demo" -TXT2LRF = "src/calibre/ebooks/lrf/txt/demo" +HTML2LRF = 'calibre/ebooks/lrf/html/demo' +TXT2LRF = 'src/calibre/ebooks/lrf/txt/demo' STAGING_HOST = 'download.calibre-ebook.com' BACKUP_HOST = 'code.calibre-ebook.com' STAGING_USER = BACKUP_USER = 'root' diff --git a/setup/vcvars.py b/setup/vcvars.py index 2461e4fc9c..99cfb16d41 100644 --- a/setup/vcvars.py +++ b/setup/vcvars.py @@ -26,8 +26,8 @@ def get_program_files_location(which=CSIDL_PROGRAM_FILESX86): def find_vswhere(): for which in (CSIDL_PROGRAM_FILESX86, CSIDL_PROGRAM_FILES): root = get_program_files_location(which) - vswhere = os.path.join(root, "Microsoft Visual Studio", "Installer", - "vswhere.exe") + vswhere = os.path.join(root, 'Microsoft Visual Studio', 'Installer', + 'vswhere.exe') if os.path.exists(vswhere): return vswhere raise SystemExit('Could not find vswhere.exe') @@ -41,24 +41,24 @@ def get_output(*cmd): def find_visual_studio(): path = get_output( find_vswhere(), - "-latest", - "-requires", - "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", - "-property", - "installationPath", - "-products", - "*" + '-latest', + '-requires', + 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', + '-property', + 'installationPath', + '-products', + '*' ).strip() - return os.path.join(path, "VC", "Auxiliary", "Build") + return os.path.join(path, 'VC', 'Auxiliary', 'Build') @lru_cache def find_msbuild(): base_path = get_output( find_vswhere(), - "-latest", - "-requires", "Microsoft.Component.MSBuild", - "-property", 'installationPath' + '-latest', + '-requires', 'Microsoft.Component.MSBuild', + '-property', 'installationPath' ).strip() return glob(os.path.join( base_path, 'MSBuild', '*', 'Bin', 'MSBuild.exe'))[0] @@ -66,10 +66,10 @@ def find_msbuild(): def find_vcvarsall(): productdir = find_visual_studio() - vcvarsall = os.path.join(productdir, "vcvarsall.bat") + vcvarsall = os.path.join(productdir, 'vcvarsall.bat') if os.path.isfile(vcvarsall): return vcvarsall - raise SystemExit("Unable to find vcvarsall.bat in productdir: " + + raise SystemExit('Unable to find vcvarsall.bat in productdir: ' + productdir) @@ -92,9 +92,9 @@ def query_process(cmd, is64bit): try: stdout, stderr = popen.communicate() if popen.wait() != 0: - raise RuntimeError(stderr.decode("mbcs")) + raise RuntimeError(stderr.decode('mbcs')) - stdout = stdout.decode("mbcs") + stdout = stdout.decode('mbcs') for line in stdout.splitlines(): if '=' not in line: continue diff --git a/setup/wincross.py b/setup/wincross.py index a8da16de41..31e688362c 100644 --- a/setup/wincross.py +++ b/setup/wincross.py @@ -88,7 +88,7 @@ class Packages: self.files_to_download.append(File(pf)) # CRT headers - add_package(f"Microsoft.VC.{self.crt_version}.CRT.Headers.base") + add_package(f'Microsoft.VC.{self.crt_version}.CRT.Headers.base') # CRT libs prefix = f'Microsoft.VC.{self.crt_version}.CRT.{arch}.'.lower() variants = {} @@ -102,7 +102,7 @@ class Packages: variants[variant] = pid add_package(variants[crt_variant]) # ATL headers - add_package(f"Microsoft.VC.{self.crt_version}.ATL.Headers.base") + add_package(f'Microsoft.VC.{self.crt_version}.ATL.Headers.base') # ATL libs add_package(f'Microsoft.VC.{self.crt_version}.ATL.{arch}.Spectre.base') add_package(f'Microsoft.VC.{self.crt_version}.ATL.{arch}.base') @@ -188,10 +188,10 @@ def download(dest_dir, manifest_version=17, manifest_type='release', manifest_pa url = f'https://aka.ms/vs/{manifest_version}/{manifest_type}/channel' print('Downloading top-level manifest from', url) tm = json.loads(urlopen(url).read()) - print("Got toplevel manifest for", (tm["info"]["productDisplayVersion"])) - for item in tm["channelItems"]: - if item.get('type') == "Manifest": - url = item["payloads"][0]["url"] + print('Got toplevel manifest for', (tm['info']['productDisplayVersion'])) + for item in tm['channelItems']: + if item.get('type') == 'Manifest': + url = item['payloads'][0]['url'] print('Downloading actual manifest...') manifest = urlopen(url).read() @@ -244,7 +244,7 @@ def extract_msi(path, dest_dir): def extract_zipfile(zf, dest_dir): - tmp = os.path.join(dest_dir, "extract") + tmp = os.path.join(dest_dir, 'extract') os.mkdir(tmp) for f in zf.infolist(): name = unquote(f.filename) @@ -259,7 +259,7 @@ def extract_vsix(path, dest_dir): print('Extracting', os.path.basename(path), '...') with TemporaryDirectory(dir=dest_dir) as tdir, ZipFile(path, 'r') as zf: extract_zipfile(zf, tdir) - contents = os.path.join(tdir, "Contents") + contents = os.path.join(tdir, 'Contents') merge_trees(contents, dest_dir) names = zf.namelist() with open(os.path.join(dest_dir, os.path.basename(path) + '.listing'), 'w') as ls: diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index 37bb1bb635..6ee432132c 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -560,15 +560,15 @@ def url_slash_cleaner(url): def human_readable(size, sep=' '): - """ Convert a size in bytes into a human readable form """ - divisor, suffix = 1, "B" + ''' Convert a size in bytes into a human readable form ''' + divisor, suffix = 1, 'B' for i, candidate in enumerate(('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB')): if size < (1 << ((i + 1) * 10)): divisor, suffix = (1 << (i * 10)), candidate break size = str(float(size)/divisor) - if size.find(".") > -1: - size = size[:size.find(".")+2] + if size.find('.') > -1: + size = size[:size.find('.')+2] if size.endswith('.0'): size = size[:-2] return size + sep + suffix diff --git a/src/calibre/constants.py b/src/calibre/constants.py index e91f24b47d..dd11ae7d3c 100644 --- a/src/calibre/constants.py +++ b/src/calibre/constants.py @@ -14,7 +14,7 @@ __appname__ = 'calibre' numeric_version = (7, 24, 101) __version__ = '.'.join(map(str, numeric_version)) git_version = None -__author__ = "Kovid Goyal " +__author__ = 'Kovid Goyal ' ''' Various run time constants. diff --git a/src/calibre/customize/__init__.py b/src/calibre/customize/__init__.py index a50a76ac94..ff4f7852e7 100644 --- a/src/calibre/customize/__init__.py +++ b/src/calibre/customize/__init__.py @@ -574,10 +574,10 @@ class CatalogPlugin(Plugin): # {{{ if requested_fields - all_fields: from calibre.library import current_library_name invalid_fields = sorted(list(requested_fields - all_fields)) - print("invalid --fields specified: %s" % ', '.join(invalid_fields)) + print('invalid --fields specified: %s' % ', '.join(invalid_fields)) print("available fields in '%s': %s" % (current_library_name(), ', '.join(sorted(list(all_fields))))) - raise ValueError("unable to generate catalog with specified fields") + raise ValueError('unable to generate catalog with specified fields') fields = [x for x in of if x in all_fields] else: @@ -600,7 +600,7 @@ class CatalogPlugin(Plugin): # {{{ from calibre.ptempfile import PersistentTemporaryDirectory if type(self) not in builtin_plugins and self.name not in config['disabled_plugins']: - files_to_copy = [f"{self.name.lower()}.{ext}" for ext in ["ui","py"]] + files_to_copy = [f'{self.name.lower()}.{ext}' for ext in ['ui','py']] resources = zipfile.ZipFile(self.plugin_path,'r') if self.resources_path is None: @@ -610,7 +610,7 @@ class CatalogPlugin(Plugin): # {{{ try: resources.extract(file, self.resources_path) except: - print(f" customize:__init__.initialize(): {file} not found in {os.path.basename(self.plugin_path)}") + print(f' customize:__init__.initialize(): {file} not found in {os.path.basename(self.plugin_path)}') continue resources.close() diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index ceac30d58c..7374ed3cec 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -1546,7 +1546,7 @@ class StoreAmazonESKindleStore(StoreBase): class StoreAmazonUKKindleStore(StoreBase): name = 'Amazon UK Kindle' author = 'Kovid Goyal' - description = 'Kindle books from Amazon\'s UK web site. Also, includes French language e-books.' + description = "Kindle books from Amazon's UK web site. Also, includes French language e-books." actual_plugin = 'calibre.gui2.store.stores.amazon_uk_plugin:AmazonKindleStore' headquarters = 'UK' @@ -1595,7 +1595,7 @@ class StoreBaenWebScriptionStore(StoreBase): class StoreBNStore(StoreBase): name = 'Barnes and Noble' - description = 'The world\'s largest book seller. As the ultimate destination for book lovers, Barnes & Noble.com offers an incredible array of content.' + description = "The world's largest book seller. As the ultimate destination for book lovers, Barnes & Noble.com offers an incredible array of content." actual_plugin = 'calibre.gui2.store.stores.bn_plugin:BNStore' headquarters = 'US' @@ -1825,7 +1825,7 @@ class StoreOzonRUStore(StoreBase): class StorePragmaticBookshelfStore(StoreBase): name = 'Pragmatic Bookshelf' - description = 'The Pragmatic Bookshelf\'s collection of programming and tech books available as e-books.' + description = "The Pragmatic Bookshelf's collection of programming and tech books available as e-books." actual_plugin = 'calibre.gui2.store.stores.pragmatic_bookshelf_plugin:PragmaticBookshelfStore' drm_free_only = True diff --git a/src/calibre/customize/zipplugin.py b/src/calibre/customize/zipplugin.py index efb988d005..34592057a2 100644 --- a/src/calibre/customize/zipplugin.py +++ b/src/calibre/customize/zipplugin.py @@ -118,8 +118,8 @@ def load_translations(namespace, zfp): _translations_cache[zfp] = None return with zipfile.ZipFile(zfp) as zf: - mo_path = zipfile.Path(zf, f"translations/{lang}.mo") - if not mo_path.exists() and "_" in lang: + mo_path = zipfile.Path(zf, f'translations/{lang}.mo') + if not mo_path.exists() and '_' in lang: mo_path = zipfile.Path(zf, f"translations/{lang.split('_')[0]}.mo") if mo_path.exists(): mo = mo_path.read_bytes() diff --git a/src/calibre/db/backend.py b/src/calibre/db/backend.py index c6ceba1e3c..f17e3cbc7c 100644 --- a/src/calibre/db/backend.py +++ b/src/calibre/db/backend.py @@ -1516,8 +1516,8 @@ class DB: @property def custom_tables(self): return {x[0] for x in self.conn.get( - 'SELECT name FROM sqlite_master WHERE type=\'table\' AND ' - '(name GLOB \'custom_column_*\' OR name GLOB \'books_custom_column_*\')')} + "SELECT name FROM sqlite_master WHERE type='table' AND " + "(name GLOB 'custom_column_*' OR name GLOB 'books_custom_column_*')")} @classmethod def exists_at(cls, path): @@ -2399,7 +2399,7 @@ class DB: text = "snippet({fts_table}, 0, ?, ?, '…', {snippet_size})".format( fts_table=fts_table, snippet_size=max(1, min(snippet_size, 64))) else: - text = f"highlight({fts_table}, 0, ?, ?)" + text = f'highlight({fts_table}, 0, ?, ?)' data.append(highlight_start) data.append(highlight_end) query = 'SELECT {0}.id, {0}.book, {0}.format, {0}.user_type, {0}.user, {0}.annot_data, {1} FROM {0} ' diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index 4c472b57cd..a0aaa293cf 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -1093,7 +1093,7 @@ class Cache: # We can't clear the composite caches because a read lock is set. # As a consequence the value of a composite column that calls # virtual_libraries() might be wrong. Oh well. Log and keep running. - print('Couldn\'t get write lock after vls_for_books_cache was loaded', file=sys.stderr) + print("Couldn't get write lock after vls_for_books_cache was loaded", file=sys.stderr) traceback.print_exc() if get_cover: diff --git a/src/calibre/db/cli/cmd_catalog.py b/src/calibre/db/cli/cmd_catalog.py index 9f75301a64..5c6fa69568 100644 --- a/src/calibre/db/cli/cmd_catalog.py +++ b/src/calibre/db/cli/cmd_catalog.py @@ -61,9 +61,9 @@ see the different options, specify the name of the output file and then the default=None, dest='ids', help=_( - "Comma-separated list of database IDs to catalog.\n" - "If declared, --search is ignored.\n" - "Default: all" + 'Comma-separated list of database IDs to catalog.\n' + 'If declared, --search is ignored.\n' + 'Default: all' ) ) parser.add_option( @@ -72,10 +72,10 @@ see the different options, specify the name of the output file and then the default=None, dest='search_text', help=_( - "Filter the results by the search query. " - "For the format of the search query, please see " - "the search-related documentation in the User Manual.\n" - "Default: no filtering" + 'Filter the results by the search query. ' + 'For the format of the search query, please see ' + 'the search-related documentation in the User Manual.\n' + 'Default: no filtering' ) ) parser.add_option( diff --git a/src/calibre/db/cli/cmd_check_library.py b/src/calibre/db/cli/cmd_check_library.py index 59852c29e3..a0812a1040 100644 --- a/src/calibre/db/cli/cmd_check_library.py +++ b/src/calibre/db/cli/cmd_check_library.py @@ -39,8 +39,8 @@ Perform some checks on the filesystem representing a library. Reports are {0} '--report', default=None, dest='report', - help=_("Comma-separated list of reports.\n" - "Default: all") + help=_('Comma-separated list of reports.\n' + 'Default: all') ) parser.add_option( @@ -48,8 +48,8 @@ Perform some checks on the filesystem representing a library. Reports are {0} '--ignore_extensions', default=None, dest='exts', - help=_("Comma-separated list of extensions to ignore.\n" - "Default: all") + help=_('Comma-separated list of extensions to ignore.\n' + 'Default: all') ) parser.add_option( @@ -57,8 +57,8 @@ Perform some checks on the filesystem representing a library. Reports are {0} '--ignore_names', default=None, dest='names', - help=_("Comma-separated list of names to ignore.\n" - "Default: all") + help=_('Comma-separated list of names to ignore.\n' + 'Default: all') ) parser.add_option( '--vacuum-fts-db', diff --git a/src/calibre/db/cli/cmd_list.py b/src/calibre/db/cli/cmd_list.py index 032b12e670..cbb62578f4 100644 --- a/src/calibre/db/cli/cmd_list.py +++ b/src/calibre/db/cli/cmd_list.py @@ -97,7 +97,7 @@ def implementation( data[field] = {k: cover(db, k) for k in book_ids} continue data[field] = db.all_field_for(field, book_ids) - return {'book_ids': book_ids, "data": data, 'metadata': metadata, 'fields':fields} + return {'book_ids': book_ids, 'data': data, 'metadata': metadata, 'fields':fields} def stringify(data, metadata, for_machine): diff --git a/src/calibre/db/cli/cmd_list_categories.py b/src/calibre/db/cli/cmd_list_categories.py index fe9822cd31..a0f3df64ad 100644 --- a/src/calibre/db/cli/cmd_list_categories.py +++ b/src/calibre/db/cli/cmd_list_categories.py @@ -54,8 +54,8 @@ information is the equivalent of what is shown in the Tag browser. '--categories', default='', dest='report', - help=_("Comma-separated list of category lookup names. " - "Default: all") + help=_('Comma-separated list of category lookup names. ' + 'Default: all') ) parser.add_option( '-w', diff --git a/src/calibre/db/cli/tests.py b/src/calibre/db/cli/tests.py index a258da3525..7c51c99f17 100644 --- a/src/calibre/db/cli/tests.py +++ b/src/calibre/db/cli/tests.py @@ -23,9 +23,9 @@ class Checker: class PrintCheckLibraryResultsTest(unittest.TestCase): - """ + ''' Asserts the format of the output to the CLI to avoid regressions - """ + ''' check = ('dummy_check', 'Dummy Check') @@ -38,11 +38,11 @@ class PrintCheckLibraryResultsTest(unittest.TestCase): self.assertEqual(stdout.getvalue(), b'') def test_human_readable_output(self): - """ + ''' Basic check of the human-readable output. Does not test: the full line format, truncation - """ + ''' data = [['first', 'second']] checker = Checker(dict.fromkeys(self.check)) setattr(checker, self.check[0], data) @@ -62,9 +62,9 @@ class PrintCheckLibraryResultsTest(unittest.TestCase): self.assertEqual(result[-1], '') def test_basic_csv_output(self): - """ + ''' Test simple csv output - """ + ''' data = [['first', 'second']] checker = Checker(dict.fromkeys(self.check)) setattr(checker, self.check[0], data) @@ -76,9 +76,9 @@ class PrintCheckLibraryResultsTest(unittest.TestCase): self.assertEqual(parsed_result, [[self.check[1], data[0][0], data[0][1]]]) def test_escaped_csv_output(self): - """ + ''' Test more complex csv output - """ + ''' data = [['I, Caesar', 'second']] checker = Checker(dict.fromkeys(self.check)) setattr(checker, self.check[0], data) diff --git a/src/calibre/db/fts/connect.py b/src/calibre/db/fts/connect.py index 32f9e04393..ce9a54dedc 100644 --- a/src/calibre/db/fts/connect.py +++ b/src/calibre/db/fts/connect.py @@ -39,7 +39,7 @@ class FTS: if conn.fts_dbpath is None: main_db_path = os.path.abspath(conn.db_filename('main')) dbpath = os.path.join(os.path.dirname(main_db_path), 'full-text-search.db') - conn.execute("ATTACH DATABASE ? AS fts_db", (dbpath,)) + conn.execute('ATTACH DATABASE ? AS fts_db', (dbpath,)) SchemaUpgrade(conn) conn.execute('UPDATE fts_db.dirtied_formats SET in_progress=FALSE WHERE in_progress=TRUE') num_dirty = conn.get('''SELECT COUNT(*) from fts_db.dirtied_formats''')[0][0] diff --git a/src/calibre/db/lazy.py b/src/calibre/db/lazy.py index 2d0d7de036..d96f033e3a 100644 --- a/src/calibre/db/lazy.py +++ b/src/calibre/db/lazy.py @@ -384,8 +384,8 @@ class ProxyMetadata(Metadata): # compatibility, flag __iter__ as unimplemented. This won't break anything # because the Metadata version raises AttributeError def __iter__(self): - raise NotImplementedError("__iter__() cannot be used in this context. " - "Use the explicit methods such as all_field_keys()") + raise NotImplementedError('__iter__() cannot be used in this context. ' + 'Use the explicit methods such as all_field_keys()') def has_key(self, key): return key in STANDARD_METADATA_FIELDS or key in ga(self, '_user_metadata') diff --git a/src/calibre/db/locking.py b/src/calibre/db/locking.py index 6b9d1fedf3..9b61f2bb9d 100644 --- a/src/calibre/db/locking.py +++ b/src/calibre/db/locking.py @@ -118,7 +118,7 @@ class SHLock: # {{{ with self._lock: if self.is_exclusive: if self._exclusive_owner is not me: - raise LockingError("release() called on unheld lock") + raise LockingError('release() called on unheld lock') self.is_exclusive -= 1 if not self.is_exclusive: self._exclusive_owner = None @@ -143,7 +143,7 @@ class SHLock: # {{{ if self._shared_owners[me] == 0: del self._shared_owners[me] except KeyError: - raise LockingError("release() called on unheld lock") + raise LockingError('release() called on unheld lock') self.is_shared -= 1 if not self.is_shared: # If there are waiting exclusive locks, @@ -156,7 +156,7 @@ class SHLock: # {{{ else: assert not self._shared_queue else: - raise LockingError("release() called on unheld lock") + raise LockingError('release() called on unheld lock') def _acquire_shared(self, blocking=True): me = current_thread() diff --git a/src/calibre/db/notes/connect.py b/src/calibre/db/notes/connect.py index 77c191fd2e..fe6c05832c 100644 --- a/src/calibre/db/notes/connect.py +++ b/src/calibre/db/notes/connect.py @@ -83,7 +83,7 @@ class Notes: def reopen(self, backend): conn = backend.get_connection() conn.notes_dbpath = os.path.join(self.notes_dir, NOTES_DB_NAME) - conn.execute("ATTACH DATABASE ? AS notes_db", (conn.notes_dbpath,)) + conn.execute('ATTACH DATABASE ? AS notes_db', (conn.notes_dbpath,)) self.allowed_fields = set() triggers = [] for table in backend.tables.values(): diff --git a/src/calibre/db/schema_upgrades.py b/src/calibre/db/schema_upgrades.py index 6e9a9ef318..1dd5a7d1d0 100644 --- a/src/calibre/db/schema_upgrades.py +++ b/src/calibre/db/schema_upgrades.py @@ -300,7 +300,7 @@ class SchemaUpgrade: for field in itervalues(self.field_metadata): if field['is_category'] and not field['is_custom'] and 'link_column' in field: table = self.db.get( - 'SELECT name FROM sqlite_master WHERE type=\'table\' AND name=?', + "SELECT name FROM sqlite_master WHERE type='table' AND name=?", ('books_%s_link'%field['table'],), all=False) if table is not None: create_tag_browser_view(field['table'], field['link_column'], field['column']) @@ -376,7 +376,7 @@ class SchemaUpgrade: for field in itervalues(self.field_metadata): if field['is_category'] and not field['is_custom'] and 'link_column' in field: table = self.db.get( - 'SELECT name FROM sqlite_master WHERE type=\'table\' AND name=?', + "SELECT name FROM sqlite_master WHERE type='table' AND name=?", ('books_%s_link'%field['table'],), all=False) if table is not None: create_std_tag_browser_view(field['table'], field['link_column'], diff --git a/src/calibre/db/tests/fts.py b/src/calibre/db/tests/fts.py index b607be21ba..8690888c83 100644 --- a/src/calibre/db/tests/fts.py +++ b/src/calibre/db/tests/fts.py @@ -83,7 +83,7 @@ class FTSTest(BaseTest): self.ae(q, expected_tokens) self.ae( - tokenize("Some wörds"), + tokenize('Some wörds'), [t('some', 0, 4), t('wörds', 5, 11), t('words', 5, 11, 1)] ) self.ae( @@ -91,20 +91,20 @@ class FTSTest(BaseTest): [t("don't", 0, 5), t('bug', 7, 10)] ) self.ae( - tokenize("a,b. c"), - [t("a", 0, 1), t('b', 2, 3), t('c', 5, 6)] + tokenize('a,b. c'), + [t('a', 0, 1), t('b', 2, 3), t('c', 5, 6)] ) self.ae( - tokenize("a*b+c"), - [t("a", 0, 1), t('b', 2, 3), t('c', 4, 5)] + tokenize('a*b+c'), + [t('a', 0, 1), t('b', 2, 3), t('c', 4, 5)] ) self.ae( - tokenize("a(b[{^c"), - [t("a", 0, 1), t('b', 2, 3), t('c', 6, 7)] + tokenize('a(b[{^c'), + [t('a', 0, 1), t('b', 2, 3), t('c', 6, 7)] ) self.ae( - tokenize("a😀smile"), - [t("a", 0, 1), t('😀', 1, 5), t('smile', 5, 10)] + tokenize('a😀smile'), + [t('a', 0, 1), t('😀', 1, 5), t('smile', 5, 10)] ) tt("你don't叫mess", '你', "don't", '叫', 'mess') @@ -125,14 +125,14 @@ class FTSTest(BaseTest): conn = TestConn() conn.insert_text('two words, and a period. With another.') conn.insert_text('and another re-init') - self.ae(conn.search("another"), [('and >another< re-init',), ('…With >another<.',)]) - self.ae(conn.search("period"), [('…a >period<. With another.',)]) + self.ae(conn.search('another'), [('and >another< re-init',), ('…With >another<.',)]) + self.ae(conn.search('period'), [('…a >period<. With another.',)]) self.ae(conn.term_row_counts(), {'a': 1, 're': 1, 'init': 1, 'and': 2, 'another': 2, 'period': 1, 'two': 1, 'with': 1, 'words': 1}) conn = TestConn() conn.insert_text('coộl') self.ae(conn.term_row_counts(), {'cool': 1, 'coộl': 1}) - self.ae(conn.search("cool"), [('>coộl<',)]) - self.ae(conn.search("coộl"), [('>coộl<',)]) + self.ae(conn.search('cool'), [('>coộl<',)]) + self.ae(conn.search('coộl'), [('>coộl<',)]) conn = TestConn(remove_diacritics=False) conn.insert_text('coộl') self.ae(conn.term_row_counts(), {'coộl': 1}) @@ -140,13 +140,13 @@ class FTSTest(BaseTest): conn = TestConn() conn.insert_text("你don't叫mess") self.ae(conn.term_row_counts(), {"don't": 1, 'mess': 1, '你': 1, '叫': 1}) - self.ae(conn.search("mess"), [("你don't叫>mess<",)]) + self.ae(conn.search('mess'), [("你don't叫>mess<",)]) self.ae(conn.search('''"don't"'''), [("你>don't<叫mess",)]) - self.ae(conn.search("你"), [(">你connection<',),]) - self.ae(conn.search("connect"), [('a simplistic >connection<',),]) - self.ae(conn.search("simplistic connect"), [('a >simplistic< >connection<',),]) - self.ae(conn.search("simplist"), [('a >simplistic< connection',),]) + self.ae(conn.search('connection'), [('a simplistic >connection<',),]) + self.ae(conn.search('connect'), [('a simplistic >connection<',),]) + self.ae(conn.search('simplistic connect'), [('a >simplistic< >connection<',),]) + self.ae(conn.search('simplist'), [('a >simplistic< connection',),]) # }}} diff --git a/src/calibre/db/tests/legacy.py b/src/calibre/db/tests/legacy.py index 82ba8b76ac..9cb4ba13c0 100644 --- a/src/calibre/db/tests/legacy.py +++ b/src/calibre/db/tests/legacy.py @@ -356,9 +356,9 @@ class LegacyTest(BaseTest): def test_legacy_adding_books(self): # {{{ 'Test various adding/deleting books methods' import sqlite3 - con = sqlite3.connect(":memory:") + con = sqlite3.connect(':memory:') try: - con.execute("create virtual table recipe using fts5(name, ingredients)") + con.execute('create virtual table recipe using fts5(name, ingredients)') except Exception: self.skipTest('python sqlite3 module does not have FTS5 support') con.close() diff --git a/src/calibre/db/tests/locking.py b/src/calibre/db/tests/locking.py index 56eaf7c52c..3c17c04358 100644 --- a/src/calibre/db/tests/locking.py +++ b/src/calibre/db/tests/locking.py @@ -22,7 +22,7 @@ def wait_for(period): class TestLock(BaseTest): - """Tests for db locking """ + '''Tests for db locking ''' def test_owns_locks(self): lock = SHLock() diff --git a/src/calibre/db/tests/reading.py b/src/calibre/db/tests/reading.py index 411f05fe7c..cbdb53eb2b 100644 --- a/src/calibre/db/tests/reading.py +++ b/src/calibre/db/tests/reading.py @@ -371,8 +371,8 @@ class ReadingTest(BaseTest): cache.set_field('timestamp', {1:p('2002-02-06'), 2:p('2000-10-06'), 3:p('2001-06-06')}) # Test numeric compare search self.assertEqual(cache.search("template:\"program: " - "floor(days_between(field(\'pubdate\'), " - "field(\'timestamp\')))#@#:n:>0\""), {2,3}) + "floor(days_between(field('pubdate'), " + "field('timestamp')))#@#:n:>0\""), {2,3}) # Test date search self.assertEqual(cache.search('template:{pubdate}#@#:d:<2001-09-01"'), {1,3}) # Test boolean search @@ -380,7 +380,7 @@ class ReadingTest(BaseTest): self.assertEqual(cache.search('template:{series}#@#:b:false'), {3}) # test primary search - cache.set_field('title', {1: "Gravity’s Raiñbow"}) + cache.set_field('title', {1: 'Gravity’s Raiñbow'}) self.assertEqual(cache.search('title:"Gravity\'s Rainbow"'), {1}) # Note that the old db searched uuid for un-prefixed searches, the new # db does not, for performance @@ -945,7 +945,7 @@ def evaluate(book, ctx): from calibre.utils.formatter_functions import load_user_template_functions, unload_user_template_functions load_user_template_functions('aaaaa', [['python_stored_template', - "", + '', 0, '''python: def evaluate(book, ctx): diff --git a/src/calibre/db/tests/writing.py b/src/calibre/db/tests/writing.py index 27ba21e845..9cc25ffa3f 100644 --- a/src/calibre/db/tests/writing.py +++ b/src/calibre/db/tests/writing.py @@ -790,7 +790,7 @@ class WritingTest(BaseTest): self.assertNotIn(uid, t.id_map) self.assertNotIn(uid, t.col_book_map) for bid in (1, 2, 3): - ae(c.field_for('publisher', bid), "mūs") + ae(c.field_for('publisher', bid), 'mūs') c.close() cache = self.init_cache() @@ -1026,17 +1026,17 @@ class WritingTest(BaseTest): self.assertEqual('url2', links['publisher']['random'], 'link for publisher random is wrong') # Check that renaming a tag keeps the link and clears the link map cache for the book - self.assertTrue(1 in cache.link_maps_cache, "book not in link_map_cache") + self.assertTrue(1 in cache.link_maps_cache, 'book not in link_map_cache') tag_id = cache.get_item_id('tags', 'foo') cache.rename_items('tags', {tag_id: 'foobar'}) - self.assertTrue(1 not in cache.link_maps_cache, "book still in link_map_cache") + self.assertTrue(1 not in cache.link_maps_cache, 'book still in link_map_cache') links = cache.get_link_map('tags') - self.assertTrue('foobar' in links, "rename foo lost the link") - self.assertEqual(links['foobar'], 'url', "The link changed contents") + self.assertTrue('foobar' in links, 'rename foo lost the link') + self.assertEqual(links['foobar'], 'url', 'The link changed contents') links = cache.get_all_link_maps_for_book(1) - self.assertTrue(1 in cache.link_maps_cache, "book not put back into link_map_cache") + self.assertTrue(1 in cache.link_maps_cache, 'book not put back into link_map_cache') self.assertDictEqual({'publisher': {'random': 'url2'}, 'tags': {'foobar': 'url'}}, - links, "book links incorrect after tag rename") + links, 'book links incorrect after tag rename') # Check ProxyMetadata mi = cache.get_proxy_metadata(1) diff --git a/src/calibre/debug.py b/src/calibre/debug.py index 36310bfe28..e0959616f8 100644 --- a/src/calibre/debug.py +++ b/src/calibre/debug.py @@ -63,7 +63,7 @@ as a shebang in scripts, like this: help=_('Run the GUI with a debug console, logging to the' ' specified path. For internal use only, use the -g' ' option to run the GUI in debug mode')) - parser.add_option('--run-without-debug', default=False, action='store_true', help=_('Don\'t run with the DEBUG flag set')) + parser.add_option('--run-without-debug', default=False, action='store_true', help=_("Don't run with the DEBUG flag set")) parser.add_option('-w', '--viewer', default=False, action='store_true', help=_('Run the E-book viewer in debug mode')) parser.add_option('--paths', default=False, action='store_true', diff --git a/src/calibre/devices/__init__.py b/src/calibre/devices/__init__.py index be170ca627..317a30e4b4 100644 --- a/src/calibre/devices/__init__.py +++ b/src/calibre/devices/__init__.py @@ -25,7 +25,7 @@ def strptime(src): def strftime(epoch, zone=time.gmtime): - src = time.strftime("%w, %d %m %Y %H:%M:%S GMT", zone(epoch)).split() + src = time.strftime('%w, %d %m %Y %H:%M:%S GMT', zone(epoch)).split() src[0] = INVERSE_DAY_MAP[int(src[0][:-1])]+',' src[2] = INVERSE_MONTH_MAP[int(src[2])] return ' '.join(src) diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index 3c75c760a2..8b73d96d12 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -212,10 +212,10 @@ class ANDROID(USBMS): EBOOK_DIR_MAIN = ['eBooks/import', 'wordplayer/calibretransfer', 'Books', 'sdcard/ebooks'] EXTRA_CUSTOMIZATION_MESSAGE = [_('Comma separated list of folders to ' - 'send e-books to on the device\'s main memory. The first one that exists will ' + "send e-books to on the device's main memory. The first one that exists will " 'be used'), _('Comma separated list of folders to ' - 'send e-books to on the device\'s storage cards. The first one that exists will ' + "send e-books to on the device's storage cards. The first one that exists will " 'be used') ] diff --git a/src/calibre/devices/cli.py b/src/calibre/devices/cli.py index ae26184675..cab3cbf152 100755 --- a/src/calibre/devices/cli.py +++ b/src/calibre/devices/cli.py @@ -3,11 +3,11 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' -""" +''' Provides a command-line interface to ebook devices. For usage information run the script. -""" +''' import os import sys @@ -37,14 +37,14 @@ class FileFormatter: @property def mode_string(self): - """ The mode string for this file. There are only two modes read-only and read-write """ - mode, x = "-", "-" + ''' The mode string for this file. There are only two modes read-only and read-write ''' + mode, x = '-', '-' if self.is_dir: - mode, x = "d", "x" + mode, x = 'd', 'x' if self.is_readonly: - mode += "r-"+x+"r-"+x+"r-"+x + mode += 'r-'+x+'r-'+x+'r-'+x else: - mode += "rw"+x+"rw"+x+"rw"+x + mode += 'rw'+x+'rw'+x+'rw'+x return mode @property @@ -57,41 +57,41 @@ class FileFormatter: @property def name_in_color(self): - """ The name in ANSI text. Directories are blue, ebooks are green """ + ''' The name in ANSI text. Directories are blue, ebooks are green ''' cname = self.name - blue, green, normal = "", "", "" + blue, green, normal = '', '', '' if self.term: blue, green, normal = self.term.BLUE, self.term.GREEN, self.term.NORMAL if self.is_dir: cname = blue + self.name + normal else: - ext = self.name[self.name.rfind("."):] - if ext in (".pdf", ".rtf", ".lrf", ".lrx", ".txt"): + ext = self.name[self.name.rfind('.'):] + if ext in ('.pdf', '.rtf', '.lrf', '.lrx', '.txt'): cname = green + self.name + normal return cname @property def human_readable_size(self): - """ File size in human readable form """ + ''' File size in human readable form ''' return human_readable(self.size) @property def modification_time(self): - """ Last modified time in the Linux ls -l format """ - return time.strftime("%Y-%m-%d %H:%M", time.localtime(self.wtime)) + ''' Last modified time in the Linux ls -l format ''' + return time.strftime('%Y-%m-%d %H:%M', time.localtime(self.wtime)) @property def creation_time(self): - """ Last modified time in the Linux ls -l format """ - return time.strftime("%Y-%m-%d %H:%M", time.localtime(self.ctime)) + ''' Last modified time in the Linux ls -l format ''' + return time.strftime('%Y-%m-%d %H:%M', time.localtime(self.ctime)) def info(dev): info = dev.get_device_information() - print("Device name: ", info[0]) - print("Device version: ", info[1]) - print("Software version:", info[2]) - print("Mime type: ", info[3]) + print('Device name: ', info[0]) + print('Device version: ', info[1]) + print('Software version:', info[2]) + print('Mime type: ', info[3]) def ls(dev, path, recurse=False, human_readable_size=False, ll=False, cols=0): @@ -115,12 +115,12 @@ def ls(dev, path, recurse=False, human_readable_size=False, ll=False, cols=0): return rowwidths output = PolyglotStringIO() - if path.endswith("/") and len(path) > 1: + if path.endswith('/') and len(path) > 1: path = path[:-1] dirs = dev.list(path, recurse) for dir in dirs: if recurse: - prints(dir[0] + ":", file=output) + prints(dir[0] + ':', file=output) lsoutput, lscoloutput = [], [] files = dir[1] maxlen = 0 @@ -141,7 +141,7 @@ def ls(dev, path, recurse=False, human_readable_size=False, ll=False, cols=0): size = str(file.size) if human_readable_size: size = file.human_readable_size - prints(file.mode_string, ("%"+str(maxlen)+"s")%size, file.modification_time, name, file=output) + prints(file.mode_string, ('%'+str(maxlen)+'s')%size, file.modification_time, name, file=output) if not ll and len(lsoutput) > 0: trytable = [] for colwidth in range(MINIMUM_COL_WIDTH, cols): @@ -163,10 +163,10 @@ def ls(dev, path, recurse=False, human_readable_size=False, ll=False, cols=0): for r in range(len(trytable)): for c in range(len(trytable[r])): padding = rowwidths[c] - len(trytable[r][c]) - prints(trytablecol[r][c], "".ljust(padding), end=' ', file=output) + prints(trytablecol[r][c], ''.ljust(padding), end=' ', file=output) prints(file=output) prints(file=output) - listing = output.getvalue().rstrip() + "\n" + listing = output.getvalue().rstrip() + '\n' output.close() return listing @@ -183,13 +183,13 @@ def main(): from calibre.utils.terminal import geometry cols = geometry()[0] - parser = OptionParser(usage="usage: %prog [options] command args\n\ncommand "+ - "is one of: info, books, df, ls, cp, mkdir, touch, cat, rm, eject, test_file\n\n"+ - "For help on a particular command: %prog command", version=__appname__+" version: " + __version__) - parser.add_option("--log-packets", help="print out packet stream to stdout. "+ - "The numbers in the left column are byte offsets that allow the packet size to be read off easily.", - dest="log_packets", action="store_true", default=False) - parser.remove_option("-h") + parser = OptionParser(usage='usage: %prog [options] command args\n\ncommand '+ + 'is one of: info, books, df, ls, cp, mkdir, touch, cat, rm, eject, test_file\n\n'+ + 'For help on a particular command: %prog command', version=__appname__+' version: ' + __version__) + parser.add_option('--log-packets', help='print out packet stream to stdout. '+ + 'The numbers in the left column are byte offsets that allow the packet size to be read off easily.', + dest='log_packets', action='store_true', default=False) + parser.remove_option('-h') parser.disable_interspersed_args() # Allow unrecognized options options, args = parser.parse_args() @@ -238,55 +238,55 @@ def main(): break try: - if command == "df": + if command == 'df': total = dev.total_space(end_session=False) free = dev.free_space() - where = ("Memory", "Card A", "Card B") - print("Filesystem\tSize \tUsed \tAvail \tUse%") + where = ('Memory', 'Card A', 'Card B') + print('Filesystem\tSize \tUsed \tAvail \tUse%') for i in range(3): - print("%-10s\t%s\t%s\t%s\t%s"%(where[i], human_readable(total[i]), human_readable(total[i]-free[i]), human_readable(free[i]), - str(0 if total[i]==0 else int(100*(total[i]-free[i])/(total[i]*1.)))+"%")) + print('%-10s\t%s\t%s\t%s\t%s'%(where[i], human_readable(total[i]), human_readable(total[i]-free[i]), human_readable(free[i]), + str(0 if total[i]==0 else int(100*(total[i]-free[i])/(total[i]*1.)))+'%')) elif command == 'eject': dev.eject() - elif command == "books": - print("Books in main memory:") + elif command == 'books': + print('Books in main memory:') for book in dev.books(): print(book) - print("\nBooks on storage carda:") + print('\nBooks on storage carda:') for book in dev.books(oncard='carda'): print(book) - print("\nBooks on storage cardb:") + print('\nBooks on storage cardb:') for book in dev.books(oncard='cardb'): print(book) - elif command == "mkdir": - parser = OptionParser(usage="usage: %prog mkdir [options] path\nCreate a folder on the device\n\npath must begin with / or card:/") + elif command == 'mkdir': + parser = OptionParser(usage='usage: %prog mkdir [options] path\nCreate a folder on the device\n\npath must begin with / or card:/') if len(args) != 1: parser.print_help() sys.exit(1) dev.mkdir(args[0]) - elif command == "ls": - parser = OptionParser(usage="usage: %prog ls [options] path\nList files on the device\n\npath must begin with / or card:/") + elif command == 'ls': + parser = OptionParser(usage='usage: %prog ls [options] path\nList files on the device\n\npath must begin with / or card:/') parser.add_option( - "-l", help="In addition to the name of each file, print the file type, permissions, and timestamp (the modification time, in the local timezone). Times are local.", # noqa: E501 - dest="ll", action="store_true", default=False) - parser.add_option("-R", help="Recursively list subfolders encountered. /dev and /proc are omitted", - dest="recurse", action="store_true", default=False) - parser.remove_option("-h") - parser.add_option("-h", "--human-readable", help="show sizes in human readable format", dest="hrs", action="store_true", default=False) + '-l', help='In addition to the name of each file, print the file type, permissions, and timestamp (the modification time, in the local timezone). Times are local.', # noqa: E501 + dest='ll', action='store_true', default=False) + parser.add_option('-R', help='Recursively list subfolders encountered. /dev and /proc are omitted', + dest='recurse', action='store_true', default=False) + parser.remove_option('-h') + parser.add_option('-h', '--human-readable', help='show sizes in human readable format', dest='hrs', action='store_true', default=False) options, args = parser.parse_args(args) if len(args) != 1: parser.print_help() return 1 print(ls(dev, args[0], recurse=options.recurse, ll=options.ll, human_readable_size=options.hrs, cols=cols), end=' ') - elif command == "info": + elif command == 'info': info(dev) - elif command == "cp": - usage="usage: %prog cp [options] source destination\nCopy files to/from the device\n\n"+\ - "One of source or destination must be a path on the device. \n\nDevice paths have the form\n"+\ - "dev:mountpoint/my/path\n"+\ - "where mountpoint is one of / or carda: or cardb:/\n\n"+\ - "source must point to a file for which you have read permissions\n"+\ - "destination must point to a file or folder for which you have write permissions" + elif command == 'cp': + usage='usage: %prog cp [options] source destination\nCopy files to/from the device\n\n'+\ + 'One of source or destination must be a path on the device. \n\nDevice paths have the form\n'+\ + 'dev:mountpoint/my/path\n'+\ + 'where mountpoint is one of / or carda: or cardb:/\n\n'+\ + 'source must point to a file for which you have read permissions\n'+\ + 'destination must point to a file or folder for which you have write permissions' parser = OptionParser(usage=usage) parser.add_option('-f', '--force', dest='force', action='store_true', default=False, help='Overwrite the destination file if it exists already.') @@ -294,15 +294,15 @@ def main(): if len(args) != 2: parser.print_help() return 1 - if args[0].startswith("dev:"): + if args[0].startswith('dev:'): outfile = args[1] path = args[0][4:] - if path.endswith("/"): + if path.endswith('/'): path = path[:-1] if os.path.isdir(outfile): - outfile = os.path.join(outfile, path[path.rfind("/")+1:]) + outfile = os.path.join(outfile, path[path.rfind('/')+1:]) try: - outfile = open(outfile, "wb") + outfile = open(outfile, 'wb') except OSError as e: print(e, file=sys.stderr) parser.print_help() @@ -310,9 +310,9 @@ def main(): dev.get_file(path, outfile) fsync(outfile) outfile.close() - elif args[1].startswith("dev:"): + elif args[1].startswith('dev:'): try: - infile = open(args[0], "rb") + infile = open(args[0], 'rb') except OSError as e: print(e, file=sys.stderr) parser.print_help() @@ -322,31 +322,31 @@ def main(): else: parser.print_help() return 1 - elif command == "cat": + elif command == 'cat': outfile = sys.stdout parser = OptionParser( - usage="usage: %prog cat path\nShow file on the device\n\npath should point to a file on the device and must begin with /,a:/ or b:/") + usage='usage: %prog cat path\nShow file on the device\n\npath should point to a file on the device and must begin with /,a:/ or b:/') options, args = parser.parse_args(args) if len(args) != 1: parser.print_help() return 1 - if args[0].endswith("/"): + if args[0].endswith('/'): path = args[0][:-1] else: path = args[0] outfile = sys.stdout dev.get_file(path, outfile) - elif command == "rm": - parser = OptionParser(usage="usage: %prog rm path\nDelete files from the device\n\npath should point to a file or empty folder on the device "+ - "and must begin with / or card:/\n\n"+ - "rm will DELETE the file. Be very CAREFUL") + elif command == 'rm': + parser = OptionParser(usage='usage: %prog rm path\nDelete files from the device\n\npath should point to a file or empty folder on the device '+ + 'and must begin with / or card:/\n\n'+ + 'rm will DELETE the file. Be very CAREFUL') options, args = parser.parse_args(args) if len(args) != 1: parser.print_help() return 1 dev.rm(args[0]) - elif command == "touch": - parser = OptionParser(usage="usage: %prog touch path\nCreate an empty file on the device\n\npath should point to a file on the device and must begin with /,a:/ or b:/\n\n"+ # noqa: E501 + elif command == 'touch': + parser = OptionParser(usage='usage: %prog touch path\nCreate an empty file on the device\n\npath should point to a file on the device and must begin with /,a:/ or b:/\n\n'+ # noqa: E501 "Unfortunately, I can't figure out how to update file times on the device, so if path already exists, touch does nothing") options, args = parser.parse_args(args) if len(args) != 1: @@ -354,7 +354,7 @@ def main(): return 1 dev.touch(args[0]) elif command == 'test_file': - parser = OptionParser(usage=("usage: %prog test_file path\n" + parser = OptionParser(usage=('usage: %prog test_file path\n' 'Open device, copy file specified by path to device and ' 'then eject device.')) options, args = parser.parse_args(args) @@ -373,7 +373,7 @@ def main(): dev.close() return 1 except DeviceLocked: - print("The device is locked. Use the --unlock option", file=sys.stderr) + print('The device is locked. Use the --unlock option', file=sys.stderr) except (ArgumentError, DeviceError) as e: print(e, file=sys.stderr) return 1 diff --git a/src/calibre/devices/cybook/t2b.py b/src/calibre/devices/cybook/t2b.py index 62ac8cdb2f..22ccbcef3c 100644 --- a/src/calibre/devices/cybook/t2b.py +++ b/src/calibre/devices/cybook/t2b.py @@ -23,7 +23,7 @@ def reduce_color(c): def i2b(n): - return "".join([str((n >> y) & 1) for y in range(1, -1, -1)]) + return ''.join([str((n >> y) & 1) for y in range(1, -1, -1)]) def write_t2b(t2bfile, coverdata=None): @@ -34,7 +34,7 @@ def write_t2b(t2bfile, coverdata=None): from PIL import Image if coverdata is not None: coverdata = io.BytesIO(coverdata) - cover = Image.open(coverdata).convert("L") + cover = Image.open(coverdata).convert('L') cover.thumbnail((96, 144), Image.Resampling.LANCZOS) t2bcover = Image.new('L', (96, 144), 'white') diff --git a/src/calibre/devices/cybook/t4b.py b/src/calibre/devices/cybook/t4b.py index ce977be27b..da5c9e51f9 100644 --- a/src/calibre/devices/cybook/t4b.py +++ b/src/calibre/devices/cybook/t4b.py @@ -21,7 +21,7 @@ def write_t4b(t4bfile, coverdata=None): from PIL import Image if coverdata is not None: coverdata = BytesIO(coverdata) - cover = Image.open(coverdata).convert("L") + cover = Image.open(coverdata).convert('L') cover.thumbnail((96, 144), Image.Resampling.LANCZOS) t4bcover = Image.new('L', (96, 144), 'white') diff --git a/src/calibre/devices/eb600/driver.py b/src/calibre/devices/eb600/driver.py index 2c0d65772a..ea09009b64 100644 --- a/src/calibre/devices/eb600/driver.py +++ b/src/calibre/devices/eb600/driver.py @@ -71,7 +71,7 @@ class TOLINO(EB600): EXTRA_CUSTOMIZATION_MESSAGE = [ _('Swap main and card A') + ':::' + - _('Check this box if the device\'s main memory is being seen as card a and the card ' + _("Check this box if the device's main memory is being seen as card a and the card " 'is being seen as main memory. Some tolino devices may need this option.'), ] diff --git a/src/calibre/devices/errors.py b/src/calibre/devices/errors.py index 7449bbcb71..bd6c2f301e 100644 --- a/src/calibre/devices/errors.py +++ b/src/calibre/devices/errors.py @@ -1,36 +1,36 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' -""" +''' Defines the errors that the device drivers generate. G{classtree ProtocolError} -""" +''' class ProtocolError(Exception): - """ The base class for all exceptions in this package """ + ''' The base class for all exceptions in this package ''' def __init__(self, msg): Exception.__init__(self, msg) class TimeoutError(ProtocolError): - """ There was a timeout during communication """ + ''' There was a timeout during communication ''' def __init__(self, func_name): ProtocolError.__init__( self, - "There was a timeout while communicating with the device in function: " + + 'There was a timeout while communicating with the device in function: ' + func_name ) class DeviceError(ProtocolError): - """ Raised when device is not found """ + ''' Raised when device is not found ''' def __init__(self, msg=None): if msg is None: - msg = "Unable to find SONY Reader. Is it connected?" + msg = 'Unable to find SONY Reader. Is it connected?' ProtocolError.__init__(self, msg) @@ -71,14 +71,14 @@ class OpenActionNeeded(DeviceError): class InitialConnectionError(OpenFeedback): - """ Errors detected during connection after detection but before open, for - e.g. in the is_connected() method. """ + ''' Errors detected during connection after detection but before open, for + e.g. in the is_connected() method. ''' class OpenFailed(ProtocolError): - """ Raised when device cannot be opened this time. No retry is to be done. + ''' Raised when device cannot be opened this time. No retry is to be done. The device should continue to be polled for future opens. If the - message is empty, no exception trace is produced. """ + message is empty, no exception trace is produced. ''' def __init__(self, msg): ProtocolError.__init__(self, msg) @@ -86,36 +86,36 @@ class OpenFailed(ProtocolError): class DeviceBusy(ProtocolError): - """ Raised when device is busy """ + ''' Raised when device is busy ''' - def __init__(self, uerr=""): + def __init__(self, uerr=''): ProtocolError.__init__( - self, "Device is in use by another application:" - "\nUnderlying error:" + str(uerr) + self, 'Device is in use by another application:' + '\nUnderlying error:' + str(uerr) ) class DeviceLocked(ProtocolError): - """ Raised when device has been locked """ + ''' Raised when device has been locked ''' def __init__(self): - ProtocolError.__init__(self, "Device is locked") + ProtocolError.__init__(self, 'Device is locked') class PacketError(ProtocolError): - """ Errors with creating/interpreting packets """ + ''' Errors with creating/interpreting packets ''' class FreeSpaceError(ProtocolError): - """ Errors caused when trying to put files onto an overcrowded device """ + ''' Errors caused when trying to put files onto an overcrowded device ''' class ArgumentError(ProtocolError): - """ Errors caused by invalid arguments to a public interface function """ + ''' Errors caused by invalid arguments to a public interface function ''' class PathError(ArgumentError): - """ When a user supplies an incorrect/invalid path """ + ''' When a user supplies an incorrect/invalid path ''' def __init__(self, msg, path=None): ArgumentError.__init__(self, msg) @@ -123,7 +123,7 @@ class PathError(ArgumentError): class ControlError(ProtocolError): - """ Errors in Command/Response pairs while communicating with the device """ + ''' Errors in Command/Response pairs while communicating with the device ''' def __init__(self, query=None, response=None, desc=None): self.query = query @@ -133,13 +133,13 @@ class ControlError(ProtocolError): def __str__(self): if self.query and self.response: - return "Got unexpected response:\n" + \ - "query:\n"+str(self.query.query)+"\n"+\ - "expected:\n"+str(self.query.response)+"\n" +\ - "actual:\n"+str(self.response) + return 'Got unexpected response:\n' + \ + 'query:\n'+str(self.query.query)+'\n'+\ + 'expected:\n'+str(self.query.response)+'\n' +\ + 'actual:\n'+str(self.response) if self.desc: return self.desc - return "Unknown control error occurred" + return 'Unknown control error occurred' class WrongDestinationError(PathError): diff --git a/src/calibre/devices/interface.py b/src/calibre/devices/interface.py index 2c2df90771..be3dc32676 100644 --- a/src/calibre/devices/interface.py +++ b/src/calibre/devices/interface.py @@ -18,14 +18,14 @@ class OpenPopupMessage: class DevicePlugin(Plugin): - """ + ''' Defines the interface that should be implemented by backends that communicate with an e-book reader. - """ + ''' type = _('Device interface') #: Ordered list of supported formats - FORMATS = ["lrf", "rtf", "pdf", "txt"] + FORMATS = ['lrf', 'rtf', 'pdf', 'txt'] # If True, the config dialog will not show the formats box HIDE_FORMATS_CONFIG_BOX = False @@ -226,7 +226,7 @@ class DevicePlugin(Plugin): def reset(self, key='-1', log_packets=False, report_progress=None, detected_device=None): - """ + ''' :param key: The key to unlock the device :param log_packets: If true the packet stream to/from the device is logged :param report_progress: Function that is called with a % progress @@ -235,7 +235,7 @@ class DevicePlugin(Plugin): task does not have any progress information :param detected_device: Device information from the device scanner - """ + ''' raise NotImplementedError() def can_handle_windows(self, usbdevice, debug=False): @@ -324,14 +324,14 @@ class DevicePlugin(Plugin): raise NotImplementedError() def get_device_information(self, end_session=True): - """ + ''' Ask device for device information. See L{DeviceInfoQuery}. :return: (device name, device version, software version on device, MIME type) The tuple can optionally have a fifth element, which is a drive information dictionary. See usbms.driver for an example. - """ + ''' raise NotImplementedError() def get_driveinfo(self): diff --git a/src/calibre/devices/jetbook/driver.py b/src/calibre/devices/jetbook/driver.py index 98c20167ea..914fc98f22 100644 --- a/src/calibre/devices/jetbook/driver.py +++ b/src/calibre/devices/jetbook/driver.py @@ -38,8 +38,8 @@ class JETBOOK(USBMS): MAIN_MEMORY_VOLUME_LABEL = 'Jetbook Main Memory' STORAGE_CARD_VOLUME_LABEL = 'Jetbook Storage Card' - EBOOK_DIR_MAIN = "Books" - EBOOK_DIR_CARD_A = "Books" + EBOOK_DIR_MAIN = 'Books' + EBOOK_DIR_CARD_A = 'Books' SUPPORTS_SUB_DIRS = True JETBOOK_FILE_NAME_PATTERN = re.compile( diff --git a/src/calibre/devices/kindle/apnx.py b/src/calibre/devices/kindle/apnx.py index 91b9edbb5d..9321b1fd53 100644 --- a/src/calibre/devices/kindle/apnx.py +++ b/src/calibre/devices/kindle/apnx.py @@ -24,9 +24,9 @@ from polyglot.builtins import as_bytes, as_unicode class APNXBuilder: - """ + ''' Create an APNX file using a pseudo page mapping. - """ + ''' generators: dict[str, IPageGenerator] = { FastPageGenerator.instance.name(): FastPageGenerator.instance, @@ -36,11 +36,11 @@ class APNXBuilder: } def write_apnx(self, mobi_file_path: str, apnx_path: str, method: str | None = None, page_count: int = 0): - """ + ''' If you want a fixed number of pages (such as from a custom column) then pass in a value to page_count, otherwise a count will be estimated using either the fast or accurate algorithm. - """ + ''' apnx_meta = self.get_apnx_meta(mobi_file_path) if page_count: diff --git a/src/calibre/devices/kindle/apnx_page_generator/generators/accurate_page_generator.py b/src/calibre/devices/kindle/apnx_page_generator/generators/accurate_page_generator.py index fa297d32bf..e657a09705 100644 --- a/src/calibre/devices/kindle/apnx_page_generator/generators/accurate_page_generator.py +++ b/src/calibre/devices/kindle/apnx_page_generator/generators/accurate_page_generator.py @@ -13,13 +13,13 @@ class AccuratePageGenerator(IPageGenerator): instance = None def name(self) -> str: - return "accurate" + return 'accurate' def _generate_fallback(self, mobi_file_path: str, real_count: int | None) -> Pages: return FastPageGenerator.instance.generate(mobi_file_path, real_count) def _generate(self, mobi_file_path: str, real_count: int | None) -> Pages: - """ + ''' A more accurate but much more resource intensive and slower method to calculate the page length. @@ -35,7 +35,7 @@ class AccuratePageGenerator(IPageGenerator): This can be make more accurate by accounting for
as a new page marker. And
elements as an empty line. - """ + ''' pages = [] html = mobi_html(mobi_file_path) diff --git a/src/calibre/devices/kindle/apnx_page_generator/generators/exact_page_generator.py b/src/calibre/devices/kindle/apnx_page_generator/generators/exact_page_generator.py index 37341b2e03..d492312426 100644 --- a/src/calibre/devices/kindle/apnx_page_generator/generators/exact_page_generator.py +++ b/src/calibre/devices/kindle/apnx_page_generator/generators/exact_page_generator.py @@ -13,17 +13,17 @@ class ExactPageGenerator(IPageGenerator): instance = None def name(self) -> str: - return "exact" + return 'exact' def _generate_fallback(self, mobi_file_path: str, real_count: int | None) -> Pages: return FastPageGenerator.instance.generate(mobi_file_path, real_count) def _generate(self, mobi_file_path: str, real_count: int | None) -> Pages: - """ + ''' Given a specified page count (such as from a custom column), create our array of pages for the apnx file by dividing by the content size of the book. - """ + ''' pages = [] count = 0 diff --git a/src/calibre/devices/kindle/apnx_page_generator/generators/fast_page_generator.py b/src/calibre/devices/kindle/apnx_page_generator/generators/fast_page_generator.py index 3a4dbce532..9027f4d1bf 100644 --- a/src/calibre/devices/kindle/apnx_page_generator/generators/fast_page_generator.py +++ b/src/calibre/devices/kindle/apnx_page_generator/generators/fast_page_generator.py @@ -10,10 +10,10 @@ from calibre.devices.kindle.apnx_page_generator.pages import Pages class FastPageGenerator(IPageGenerator): def name(self) -> str: - return "fast" + return 'fast' def _generate_fallback(self, mobi_file_path: str, real_count: int | None) -> Pages: - raise Exception("Fast calculation impossible.") + raise Exception('Fast calculation impossible.') def _generate(self, mobi_file_path: str, real_count: int | None) -> Pages: """ diff --git a/src/calibre/devices/kindle/apnx_page_generator/generators/pagebreak_page_generator.py b/src/calibre/devices/kindle/apnx_page_generator/generators/pagebreak_page_generator.py index e71346bd77..433961b1eb 100644 --- a/src/calibre/devices/kindle/apnx_page_generator/generators/pagebreak_page_generator.py +++ b/src/calibre/devices/kindle/apnx_page_generator/generators/pagebreak_page_generator.py @@ -12,13 +12,13 @@ from calibre.devices.kindle.apnx_page_generator.pages import Pages class PagebreakPageGenerator(IPageGenerator): def name(self) -> str: - return "pagebreak" + return 'pagebreak' def _generate_fallback(self, mobi_file_path: str, real_count: int | None) -> Pages: return FastPageGenerator.instance.generate(mobi_file_path, real_count) def _generate(self, mobi_file_path: str, real_count: int | None) -> Pages: - """ Determine pages based on the presence of <*pagebreak*/>. """ + ''' Determine pages based on the presence of <*pagebreak*/>. ''' html = mobi_html(mobi_file_path) pages = [] for m in re.finditer(b'<[^>]*pagebreak[^>]*>', html): diff --git a/src/calibre/devices/kindle/apnx_page_generator/i_page_generator.py b/src/calibre/devices/kindle/apnx_page_generator/i_page_generator.py index 315ce59fe6..5388c96ee9 100644 --- a/src/calibre/devices/kindle/apnx_page_generator/i_page_generator.py +++ b/src/calibre/devices/kindle/apnx_page_generator/i_page_generator.py @@ -28,7 +28,7 @@ class IPageGenerator(metaclass=ABCMeta): return result return self._generate_fallback(mobi_file_path, real_count) except Exception as e: - if self.__class__.__name__ == "FastPageGenerator": + if self.__class__.__name__ == 'FastPageGenerator': raise e return self._generate_fallback(mobi_file_path, real_count) @@ -41,7 +41,7 @@ def mobi_html(mobi_file_path: str) -> bytes: from calibre.ebooks.mobi.reader.mobi6 import MobiReader mr = MobiReader(mobi_file_path, default_log) if mr.book_header.encryption_type != 0: - raise Exception("DRMed book") + raise Exception('DRMed book') mr.extract_text() return as_bytes(mr.mobi_html.lower()) diff --git a/src/calibre/devices/kindle/apnx_page_generator/page_group.py b/src/calibre/devices/kindle/apnx_page_generator/page_group.py index e9504a0ad7..1b777756a4 100644 --- a/src/calibre/devices/kindle/apnx_page_generator/page_group.py +++ b/src/calibre/devices/kindle/apnx_page_generator/page_group.py @@ -7,7 +7,7 @@ from calibre.devices.kindle.apnx_page_generator.page_number_type import PageNumb class PageGroup: - """Simulate constructor overloading""" + '''Simulate constructor overloading''' def __init__(self, page_locations: int | list[int], page_number_type: PageNumberTypes, first_value: int, page_labels: str | list[str] | None = None): if page_locations.__class__ is int: @@ -52,5 +52,5 @@ class PageGroup: if self.__page_number_type != PageNumberTypes.Custom: values = str(self.__first_value) else: - values = "|".join(self.__page_number_labels) - return f"({starting_location},{self.__page_number_type.value},{values})" + values = '|'.join(self.__page_number_labels) + return f'({starting_location},{self.__page_number_type.value},{values})' diff --git a/src/calibre/devices/kindle/apnx_page_generator/page_number_type.py b/src/calibre/devices/kindle/apnx_page_generator/page_number_type.py index 4f468ab204..e8c49573b1 100644 --- a/src/calibre/devices/kindle/apnx_page_generator/page_number_type.py +++ b/src/calibre/devices/kindle/apnx_page_generator/page_number_type.py @@ -6,6 +6,6 @@ import enum class PageNumberTypes(enum.Enum): - Arabic = "a" - Roman = "r" - Custom = "c" + Arabic = 'a' + Roman = 'r' + Custom = 'c' diff --git a/src/calibre/devices/kindle/apnx_page_generator/pages.py b/src/calibre/devices/kindle/apnx_page_generator/pages.py index 37f4a6528b..6960c856f4 100644 --- a/src/calibre/devices/kindle/apnx_page_generator/pages.py +++ b/src/calibre/devices/kindle/apnx_page_generator/pages.py @@ -30,7 +30,7 @@ class Pages: for group in self.__pages_groups: result.append(group.get_page_map(location)) location += group.number_of_pages - return ",".join(result) + return ','.join(result) @property def page_locations(self) -> list[int]: diff --git a/src/calibre/devices/kindle/bookmark.py b/src/calibre/devices/kindle/bookmark.py index b794c963e5..61e8671f01 100644 --- a/src/calibre/devices/kindle/bookmark.py +++ b/src/calibre/devices/kindle/bookmark.py @@ -75,14 +75,14 @@ class Bookmark(): # {{{ entry_type = None rec_len, = unpack('>I', data[eo+4:eo+8]) if rec_len == 0: - current_block = "empty_data" - elif data[eo+8:eo+12] == b"EBAR": - current_block = "data_header" + current_block = 'empty_data' + elif data[eo+8:eo+12] == b'EBAR': + current_block = 'data_header' # entry_type = "data_header" location, = unpack('>I', data[eo+0x34:eo+0x38]) # print "data_header location: %d" % location else: - current_block = "text_block" + current_block = 'text_block' if previous_block == 'empty_data': entry_type = 'Note' elif previous_block == 'data_header': @@ -149,7 +149,7 @@ class Bookmark(): # {{{ mi = get_topaz_metadata(stream) my_clippings = self.path split = my_clippings.find('documents') + len('documents/') - my_clippings = my_clippings[:split] + "My Clippings.txt" + my_clippings = my_clippings[:split] + 'My Clippings.txt' try: with open(my_clippings, encoding='utf-8', errors='replace') as f2: marker_found = 0 @@ -274,7 +274,7 @@ class Bookmark(): # {{{ self.last_read_location = self.last_read - self.pdf_page_offset else: - print("unsupported bookmark_extension: %s" % self.bookmark_extension) + print('unsupported bookmark_extension: %s' % self.bookmark_extension) self.user_notes = user_notes def get_book_length(self): @@ -303,6 +303,6 @@ class Bookmark(): # {{{ except: pass else: - print("unsupported bookmark_extension: %s" % self.bookmark_extension) + print('unsupported bookmark_extension: %s' % self.bookmark_extension) # }}} diff --git a/src/calibre/devices/kindle/driver.py b/src/calibre/devices/kindle/driver.py index 9b022966ac..b5c53e479b 100644 --- a/src/calibre/devices/kindle/driver.py +++ b/src/calibre/devices/kindle/driver.py @@ -264,12 +264,12 @@ class KINDLE(USBMS): # Add the last-read location if bookmark.book_format == 'pdf': - markup = _("%(time)s
Last page read: %(loc)d (%(pr)d%%)") % dict( + markup = _('%(time)s
Last page read: %(loc)d (%(pr)d%%)') % dict( time=strftime('%x', timestamp.timetuple()), loc=last_read_location, pr=percent_read) else: - markup = _("%(time)s
Last page read: Location %(loc)d (%(pr)d%%)") % dict( + markup = _('%(time)s
Last page read: Location %(loc)d (%(pr)d%%)') % dict( time=strftime('%x', timestamp.timetuple()), loc=last_read_location, pr=percent_read) @@ -610,7 +610,7 @@ class KINDLE2(KINDLE): # Create the sidecar folder if necessary if (self.sidecar_apnx): - path = os.path.join(os.path.dirname(filepath), filename+".sdr") + path = os.path.join(os.path.dirname(filepath), filename+'.sdr') if not os.path.exists(path): os.makedirs(path) @@ -636,7 +636,7 @@ class KINDLE2(KINDLE): if temp in self.EXTRA_CUSTOMIZATION_CHOICES[self.OPT_APNX_METHOD]: method = temp else: - print("Invalid method choice for this book (%r), ignoring." % temp) + print('Invalid method choice for this book (%r), ignoring.' % temp) except: print('Could not retrieve override method choice, using default.') apnx_builder.write_apnx(filepath, apnx_path, method=method, page_count=custom_page_count) diff --git a/src/calibre/devices/kobo/bookmark.py b/src/calibre/devices/kobo/bookmark.py index 75b53c9f06..75c437225d 100644 --- a/src/calibre/devices/kobo/bookmark.py +++ b/src/calibre/devices/kobo/bookmark.py @@ -54,7 +54,7 @@ class Bookmark(): # {{{ 'ORDER BY bm.ContentID, bm.chapterprogress' ) - debug_print(f"Kobo::Bookmark::get_bookmark_data - getting kepub chapters: contentId={self.contentId}") + debug_print(f'Kobo::Bookmark::get_bookmark_data - getting kepub chapters: contentId={self.contentId}') cursor.execute(kepub_chapter_query, book_query_values) kepub_chapters = {} if self.kepub: @@ -66,9 +66,9 @@ class Bookmark(): # {{{ 'chapter_title': chapter_row['Title'], 'chapter_index': chapter_row['VolumeIndex'] } - debug_print(f"Kobo::Bookmark::get_bookmark_data - getting kepub chapter: kepub chapters={kepub_chapters}") + debug_print(f'Kobo::Bookmark::get_bookmark_data - getting kepub chapter: kepub chapters={kepub_chapters}') except: - debug_print("Kobo::Bookmark::get_bookmark_data - No chapters found") + debug_print('Kobo::Bookmark::get_bookmark_data - No chapters found') cursor.execute(bookmark_query, book_query_values) @@ -92,7 +92,7 @@ class Bookmark(): # {{{ debug_print(f"Kobo::Bookmark::get_bookmark_data - getting kepub: chapter file_contentID_part='{file_contentID_part}'") # from urllib import quote # file_contentID_part = quote(file_contentID_part) - chapter_contentID = book_contentID_part + "!" + opf_reference + "!" + file_contentID_part + chapter_contentID = book_contentID_part + '!' + opf_reference + '!' + file_contentID_part debug_print(f"Kobo::Bookmark::get_bookmark_data - getting kepub chapter chapter_contentID='{chapter_contentID}'") kepub_chapter = kepub_chapters.get(chapter_contentID, None) if kepub_chapter is not None: @@ -115,7 +115,7 @@ class Bookmark(): # {{{ e_type = 'Bookmark' text = row['Title'] # highlight is text with no annotation - elif text is not None and (annotation is None or annotation == ""): + elif text is not None and (annotation is None or annotation == ''): e_type = 'Highlight' elif text and annotation: e_type = 'Annotation' @@ -165,7 +165,7 @@ class Bookmark(): # {{{ A string representation of this object, suitable for printing to console ''' - ans = ["Kobo bookmark:"] + ans = ['Kobo bookmark:'] def fmt(x, y): ans.append('%-20s: %s'%(str(x), str(y))) @@ -181,7 +181,7 @@ class Bookmark(): # {{{ if self.user_notes: fmt('User Notes', self.user_notes) - ans = '\n'.join(ans) + "\n" + ans = '\n'.join(ans) + '\n' return ans diff --git a/src/calibre/devices/kobo/books.py b/src/calibre/devices/kobo/books.py index 4e4f8b5aac..e8d62a67d2 100644 --- a/src/calibre/devices/kobo/books.py +++ b/src/calibre/devices/kobo/books.py @@ -22,13 +22,13 @@ class Book(Book_): thumbnail_name=None, size=None, other=None): from calibre.utils.date import parse_date # debug_print('Book::__init__ - title=', title) - show_debug = title is not None and title.lower().find("xxxxx") >= 0 + show_debug = title is not None and title.lower().find('xxxxx') >= 0 if other is not None: other.title = title other.published_date = date if show_debug: - debug_print("Book::__init__ - title=", title, 'authors=', authors) - debug_print("Book::__init__ - other=", other) + debug_print('Book::__init__ - title=', title, 'authors=', authors) + debug_print('Book::__init__ - other=', other) super().__init__(prefix, lpath, size, other) if title is not None and len(title) > 0: @@ -36,7 +36,7 @@ class Book(Book_): if authors is not None and len(authors) > 0: self.authors_from_string(authors) - if self.author_sort is None or self.author_sort == "Unknown": + if self.author_sort is None or self.author_sort == 'Unknown': self.author_sort = author_to_author_sort(authors) self.mime = mime @@ -45,13 +45,13 @@ class Book(Book_): if ContentType == '6' and date is not None: try: - self.datetime = time.strptime(date, "%Y-%m-%dT%H:%M:%S.%f") + self.datetime = time.strptime(date, '%Y-%m-%dT%H:%M:%S.%f') except: try: - self.datetime = time.strptime(date.split('+')[0], "%Y-%m-%dT%H:%M:%S") + self.datetime = time.strptime(date.split('+')[0], '%Y-%m-%dT%H:%M:%S') except: try: - self.datetime = time.strptime(date.split('+')[0], "%Y-%m-%d") + self.datetime = time.strptime(date.split('+')[0], '%Y-%m-%d') except: try: self.datetime = parse_date(date, @@ -77,13 +77,13 @@ class Book(Book_): self.thumbnail = ImageWrapper(thumbnail_name) if show_debug: - debug_print("Book::__init__ end - self=", self) - debug_print("Book::__init__ end - title=", title, 'authors=', authors) + debug_print('Book::__init__ end - self=', self) + debug_print('Book::__init__ end - title=', title, 'authors=', authors) @property def is_sideloaded(self): # If we don't have a content Id, we don't know what type it is. - return self.contentID and self.contentID.startswith("file") + return self.contentID and self.contentID.startswith('file') @property def has_kobo_series(self): @@ -91,14 +91,14 @@ class Book(Book_): @property def is_purchased_kepub(self): - return self.contentID and not self.contentID.startswith("file") + return self.contentID and not self.contentID.startswith('file') def __str__(self): ''' A string representation of this object, suitable for printing to console ''' - ans = ["Kobo metadata:"] + ans = ['Kobo metadata:'] def fmt(x, y): ans.append('%-20s: %s'%(str(x), str(y))) @@ -118,7 +118,7 @@ class Book(Book_): ans = '\n'.join(ans) - return super().__str__() + "\n" + ans + return super().__str__() + '\n' + ans class ImageWrapper: @@ -134,7 +134,7 @@ class KTCollectionsBookList(CollectionsBookList): self.set_device_managed_collections([]) def get_collections(self, collection_attributes, collections_template=None, template_globals=None): - debug_print("KTCollectionsBookList:get_collections - start - collection_attributes=", collection_attributes) + debug_print('KTCollectionsBookList:get_collections - start - collection_attributes=', collection_attributes) collections = {} @@ -142,7 +142,7 @@ class KTCollectionsBookList(CollectionsBookList): for c in collection_attributes: ca.append(c.lower()) collection_attributes = ca - debug_print("KTCollectionsBookList:get_collections - collection_attributes=", collection_attributes) + debug_print('KTCollectionsBookList:get_collections - collection_attributes=', collection_attributes) for book in self: tsval = book.get('title_sort', book.title) @@ -151,7 +151,7 @@ class KTCollectionsBookList(CollectionsBookList): show_debug = self.is_debugging_title(tsval) or tsval is None if show_debug: # or len(book.device_collections) > 0: - debug_print('KTCollectionsBookList:get_collections - tsval=', tsval, "book.title=", book.title, "book.title_sort=", book.title_sort) + debug_print('KTCollectionsBookList:get_collections - tsval=', tsval, 'book.title=', book.title, 'book.title_sort=', book.title_sort) debug_print('KTCollectionsBookList:get_collections - book.device_collections=', book.device_collections) # debug_print(book) # Make sure we can identify this book via the lpath @@ -168,7 +168,7 @@ class KTCollectionsBookList(CollectionsBookList): # book in all existing collections. Do not add any new ones. attrs = ['device_collections'] if getattr(book, '_new_book', False): - debug_print("KTCollectionsBookList:get_collections - sending new book") + debug_print('KTCollectionsBookList:get_collections - sending new book') if prefs['manage_device_metadata'] == 'manual': # Ensure that the book is in all the book's existing # collections plus all metadata collections @@ -187,14 +187,14 @@ class KTCollectionsBookList(CollectionsBookList): if cat_name not in collections: collections[cat_name] = {} if show_debug: - debug_print("KTCollectionsBookList:get_collections - Device Managed Collection:", cat_name) + debug_print('KTCollectionsBookList:get_collections - Device Managed Collection:', cat_name) if lpath not in collections[cat_name]: collections[cat_name][lpath] = book if show_debug: - debug_print("KTCollectionsBookList:get_collections - Device Managed Collection -added book to cat_name", cat_name) + debug_print('KTCollectionsBookList:get_collections - Device Managed Collection -added book to cat_name', cat_name) book.device_collections = [] if show_debug: - debug_print("KTCollectionsBookList:get_collections - attrs=", attrs) + debug_print('KTCollectionsBookList:get_collections - attrs=', attrs) if collections_template is not None: attrs.append('%template%') @@ -212,7 +212,7 @@ class KTCollectionsBookList(CollectionsBookList): doing_dc = True val = book.device_collections # is a list if show_debug: - debug_print("KTCollectionsBookList:get_collections - adding book.device_collections", book.device_collections) + debug_print('KTCollectionsBookList:get_collections - adding book.device_collections', book.device_collections) elif attr == '%template%': doing_dc = False val = '' @@ -220,7 +220,7 @@ class KTCollectionsBookList(CollectionsBookList): nv = SafeFormat().safe_format(collections_template, book, 'KOBO', book, global_vars=template_globals) if show_debug: - debug_print("KTCollectionsBookList:get_collections collections_template - result", nv) + debug_print('KTCollectionsBookList:get_collections collections_template - result', nv) if nv: val = [v.strip() for v in nv.split(':@:') if v.strip()] else: @@ -228,7 +228,7 @@ class KTCollectionsBookList(CollectionsBookList): ign, val, orig_val, fm = book.format_field_extended(attr) val = book.get(attr, None) if show_debug: - debug_print("KTCollectionsBookList:get_collections - not device_collections") + debug_print('KTCollectionsBookList:get_collections - not device_collections') debug_print(' ign=', ign, ', val=', val, ' orig_val=', orig_val, 'fm=', fm) debug_print(' val=', val) @@ -249,16 +249,16 @@ class KTCollectionsBookList(CollectionsBookList): else: val = [orig_val] if show_debug: - debug_print("KTCollectionsBookList:get_collections - val is text and multiple", val) + debug_print('KTCollectionsBookList:get_collections - val is text and multiple', val) elif fm is not None and fm['datatype'] == 'composite' and fm['is_multiple']: if show_debug: - debug_print("KTCollectionsBookList:get_collections - val is compositeand multiple", val) + debug_print('KTCollectionsBookList:get_collections - val is compositeand multiple', val) val = [v.strip() for v in val.split(fm['is_multiple']['ui_to_list'])] else: val = [val] if show_debug: - debug_print("KTCollectionsBookList:get_collections - val=", val) + debug_print('KTCollectionsBookList:get_collections - val=', val) for category in val: # debug_print("KTCollectionsBookList:get_collections - category=", category) @@ -282,13 +282,13 @@ class KTCollectionsBookList(CollectionsBookList): if cat_name not in collections: collections[cat_name] = {} if show_debug: - debug_print("KTCollectionsBookList:get_collections - created collection for cat_name", cat_name) + debug_print('KTCollectionsBookList:get_collections - created collection for cat_name', cat_name) if lpath not in collections[cat_name]: collections[cat_name][lpath] = book if show_debug: - debug_print("KTCollectionsBookList:get_collections - added book to collection for cat_name", cat_name) + debug_print('KTCollectionsBookList:get_collections - added book to collection for cat_name', cat_name) if show_debug: - debug_print("KTCollectionsBookList:get_collections - cat_name", cat_name) + debug_print('KTCollectionsBookList:get_collections - cat_name', cat_name) # Sort collections result = {} @@ -296,7 +296,7 @@ class KTCollectionsBookList(CollectionsBookList): for category, lpaths in collections.items(): result[category] = lpaths.values() # debug_print("KTCollectionsBookList:get_collections - result=", result.keys()) - debug_print("KTCollectionsBookList:get_collections - end") + debug_print('KTCollectionsBookList:get_collections - end') return result def set_device_managed_collections(self, collection_names): diff --git a/src/calibre/devices/kobo/driver.py b/src/calibre/devices/kobo/driver.py index d1a7b739ae..44fdde439e 100644 --- a/src/calibre/devices/kobo/driver.py +++ b/src/calibre/devices/kobo/driver.py @@ -37,7 +37,7 @@ from polyglot.builtins import iteritems, itervalues, string_or_bytes EPUB_EXT = '.epub' KEPUB_EXT = '.kepub' -KOBO_ROOT_DIR_NAME = ".kobo" +KOBO_ROOT_DIR_NAME = '.kobo' DEFAULT_COVER_LETTERBOX_COLOR = '#000000' @@ -45,11 +45,11 @@ DEFAULT_COVER_LETTERBOX_COLOR = '#000000' def qhash(inputstr): - instr = b"" + instr = b'' if isinstance(inputstr, bytes): instr = inputstr elif isinstance(inputstr, str): - instr = inputstr.encode("utf8") + instr = inputstr.encode('utf8') else: return -1 @@ -206,15 +206,15 @@ class KOBO(USBMS): return dbversion def device_version_info(self): - debug_print("device_version_info - start") + debug_print('device_version_info - start') if not self._device_version_info: - version_file = os.path.join(self._main_prefix, KOBO_ROOT_DIR_NAME, "version") - debug_print(f"device_version_info - version_file={version_file}") + version_file = os.path.join(self._main_prefix, KOBO_ROOT_DIR_NAME, 'version') + debug_print(f'device_version_info - version_file={version_file}') if os.path.isfile(version_file): - debug_print("device_version_info - have opened version_file") + debug_print('device_version_info - have opened version_file') with open(version_file) as vf: - self._device_version_info = vf.read().strip().split(",") - debug_print("device_version_info - self._device_version_info=", self._device_version_info) + self._device_version_info = vf.read().strip().split(',') + debug_print('device_version_info - self._device_version_info=', self._device_version_info) return self._device_version_info def device_serial_no(self): @@ -334,7 +334,7 @@ class KOBO(USBMS): # print "Image name Normalized: " + imagename if not os.path.exists(imagename): - debug_print("Strange - The image name does not exist - title: ", title) + debug_print('Strange - The image name does not exist - title: ', title) if imagename is not None: bl[idx].thumbnail = ImageWrapper(imagename) if (ContentType != '6' and MimeType != 'Shortcover'): @@ -343,7 +343,7 @@ class KOBO(USBMS): # print 'update_metadata_item returned true' changed = True else: - debug_print(" Strange: The file: ", prefix, lpath, " does not exist!") + debug_print(' Strange: The file: ', prefix, lpath, ' does not exist!') if lpath in playlist_map and \ playlist_map[lpath] not in bl[idx].device_collections: bl[idx].device_collections = playlist_map.get(lpath,[]) @@ -355,13 +355,13 @@ class KOBO(USBMS): if os.path.exists(self.normalize_path(os.path.join(prefix, lpath))): book = self.book_from_path(prefix, lpath, title, authors, mime, date, ContentType, ImageID) else: - debug_print(" Strange: The file: ", prefix, lpath, " does not exist!") - title = "FILE MISSING: " + title + debug_print(' Strange: The file: ', prefix, lpath, ' does not exist!') + title = 'FILE MISSING: ' + title book = self.book_class(prefix, lpath, title, authors, mime, date, ContentType, ImageID, size=1048576) except: - debug_print("prefix: ", prefix, "lpath: ", lpath, "title: ", title, "authors: ", authors, - "mime: ", mime, "date: ", date, "ContentType: ", ContentType, "ImageID: ", ImageID) + debug_print('prefix: ', prefix, 'lpath: ', lpath, 'title: ', title, 'authors: ', authors, + 'mime: ', mime, 'date: ', date, 'ContentType: ', ContentType, 'ImageID: ', ImageID) raise # print 'Update booklist' @@ -377,7 +377,7 @@ class KOBO(USBMS): with closing(self.device_database_connection(use_row_factory=True)) as connection: self.dbversion = self.get_database_version(connection) - debug_print("Database Version: ", self.dbversion) + debug_print('Database Version: ', self.dbversion) cursor = connection.cursor() opts = self.settings() @@ -387,7 +387,7 @@ class KOBO(USBMS): 'BookID is Null %(previews)s %(recommendations)s and not ((___ExpirationStatus=3 or ___ExpirationStatus is Null) %(expiry)s') % dict( expiry=' and ContentType = 6)' if opts.extra_customization[self.OPT_SHOW_EXPIRED_BOOK_RECORDS] else ')', previews=' and Accessibility <> 6' if not self.show_previews else '', - recommendations=' and IsDownloaded in (\'true\', 1)' if opts.extra_customization[self.OPT_SHOW_RECOMMENDATIONS] is False else '') + recommendations=" and IsDownloaded in ('true', 1)" if opts.extra_customization[self.OPT_SHOW_RECOMMENDATIONS] is False else '') elif self.dbversion >= 16 and self.dbversion < 33: query= ('select Title, Attribution, DateCreated, ContentID, MimeType, ContentType, ' 'ImageID, ReadStatus, ___ExpirationStatus, FavouritesIndex, Accessibility, "1" as IsDownloaded from content where ' @@ -423,15 +423,15 @@ class KOBO(USBMS): changed = False for row in cursor: # self.report_progress((i+1) / float(numrows), _('Getting list of books on device...')) - if not hasattr(row['ContentID'], 'startswith') or row['ContentID'].startswith("file:///usr/local/Kobo/help/"): + if not hasattr(row['ContentID'], 'startswith') or row['ContentID'].startswith('file:///usr/local/Kobo/help/'): # These are internal to the Kobo device and do not exist continue path = self.path_from_contentid(row['ContentID'], row['ContentType'], row['MimeType'], oncard) mime = mime_type_ext(path_to_ext(path)) if path.find('kepub') == -1 else 'application/epub+zip' # debug_print("mime:", mime) - if oncard != 'carda' and oncard != 'cardb' and not row['ContentID'].startswith("file:///mnt/sd/"): + if oncard != 'carda' and oncard != 'cardb' and not row['ContentID'].startswith('file:///mnt/sd/'): prefix = self._main_prefix - elif oncard == 'carda' and row['ContentID'].startswith("file:///mnt/sd/"): + elif oncard == 'carda' and row['ContentID'].startswith('file:///mnt/sd/'): prefix = self._card_a_prefix changed = update_booklist(self._main_prefix, path, row['Title'], row['Attribution'], mime, row['DateCreated'], row['ContentType'], @@ -514,26 +514,26 @@ class KOBO(USBMS): cursor.execute('delete from content where BookID = ?', t) if ContentType == 6: try: - cursor.execute('update content set ReadStatus=0, FirstTimeReading = \'true\', ___PercentRead=0, ___ExpirationStatus=3 ' + cursor.execute("update content set ReadStatus=0, FirstTimeReading = 'true', ___PercentRead=0, ___ExpirationStatus=3 " 'where BookID is Null and ContentID =?',t) except Exception as e: if 'no such column' not in str(e): raise try: - cursor.execute('update content set ReadStatus=0, FirstTimeReading = \'true\', ___PercentRead=0 ' + cursor.execute("update content set ReadStatus=0, FirstTimeReading = 'true', ___PercentRead=0 " 'where BookID is Null and ContentID =?',t) except Exception as e: if 'no such column' not in str(e): raise - cursor.execute('update content set ReadStatus=0, FirstTimeReading = \'true\' ' + cursor.execute("update content set ReadStatus=0, FirstTimeReading = 'true' " 'where BookID is Null and ContentID =?',t) else: cursor.execute('delete from content where BookID is Null and ContentID =?',t) cursor.close() if ImageID is None: - print("Error condition ImageID was not found") - print("You likely tried to delete a book that the kobo has not yet added to the database") + print('Error condition ImageID was not found') + print('You likely tried to delete a book that the kobo has not yet added to the database') # If all this succeeds we need to delete the images files via the ImageID return ImageID @@ -555,7 +555,7 @@ class KOBO(USBMS): os.unlink(fpath) def delete_books(self, paths, end_session=True): - if self.modify_database_check("delete_books") is False: + if self.modify_database_check('delete_books') is False: return for i, path in enumerate(paths): @@ -594,7 +594,7 @@ class KOBO(USBMS): self.report_progress(1.0, _('Removing books from device...')) def remove_books_from_metadata(self, paths, booklists): - if self.modify_database_check("remove_books_from_metatata") is False: + if self.modify_database_check('remove_books_from_metatata') is False: return for i, path in enumerate(paths): @@ -608,12 +608,12 @@ class KOBO(USBMS): self.report_progress(1.0, _('Removing books from device metadata listing...')) def add_books_to_metadata(self, locations, metadata, booklists): - debug_print("KoboTouch::add_books_to_metadata - start. metadata=%s" % metadata[0]) + debug_print('KoboTouch::add_books_to_metadata - start. metadata=%s' % metadata[0]) metadata = iter(metadata) for i, location in enumerate(locations): self.report_progress((i+1) / float(len(locations)), _('Adding books to device metadata listing...')) info = next(metadata) - debug_print("KoboTouch::add_books_to_metadata - info=%s" % info) + debug_print('KoboTouch::add_books_to_metadata - info=%s' % info) blist = 2 if location[1] == 'cardb' else 1 if location[1] == 'carda' else 0 # Extract the correct prefix from the pathname. To do this correctly, @@ -645,7 +645,7 @@ class KOBO(USBMS): book.size = os.stat(self.normalize_path(path)).st_size b = booklists[blist].add_book(book, replace_metadata=True) if b: - debug_print("KoboTouch::add_books_to_metadata - have a new book - book=%s" % book) + debug_print('KoboTouch::add_books_to_metadata - have a new book - book=%s' % book) b._new_book = True self.report_progress(1.0, _('Adding books to device metadata listing...')) @@ -664,15 +664,15 @@ class KOBO(USBMS): ContentID = ContentID.replace(self._card_a_prefix, '') elif ContentType == 999: # HTML Files ContentID = path - ContentID = ContentID.replace(self._main_prefix, "/mnt/onboard/") + ContentID = ContentID.replace(self._main_prefix, '/mnt/onboard/') if self._card_a_prefix is not None: - ContentID = ContentID.replace(self._card_a_prefix, "/mnt/sd/") + ContentID = ContentID.replace(self._card_a_prefix, '/mnt/sd/') else: # ContentType = 16 ContentID = path - ContentID = ContentID.replace(self._main_prefix, "file:///mnt/onboard/") + ContentID = ContentID.replace(self._main_prefix, 'file:///mnt/onboard/') if self._card_a_prefix is not None: - ContentID = ContentID.replace(self._card_a_prefix, "file:///mnt/sd/") - ContentID = ContentID.replace("\\", '/') + ContentID = ContentID.replace(self._card_a_prefix, 'file:///mnt/sd/') + ContentID = ContentID.replace('\\', '/') return ContentID def get_content_type_from_path(self, path): @@ -707,28 +707,28 @@ class KOBO(USBMS): if oncard == 'cardb': print('path from_contentid cardb') elif oncard == 'carda': - path = path.replace("file:///mnt/sd/", self._card_a_prefix) + path = path.replace('file:///mnt/sd/', self._card_a_prefix) # print "SD Card: " + path else: - if ContentType == "6" and MimeType == 'Shortcover': + if ContentType == '6' and MimeType == 'Shortcover': # This is a hack as the kobo files do not exist # but the path is required to make a unique id # for calibre's reference path = self._main_prefix + path + '.kobo' # print "Path: " + path - elif (ContentType == "6" or ContentType == "10") and ( + elif (ContentType == '6' or ContentType == '10') and ( MimeType == 'application/x-kobo-epub+zip' or ( MimeType == 'application/epub+zip' and self.isTolinoDevice()) ): - if path.startswith("file:///mnt/onboard/"): - path = self._main_prefix + path.replace("file:///mnt/onboard/", '') + if path.startswith('file:///mnt/onboard/'): + path = self._main_prefix + path.replace('file:///mnt/onboard/', '') else: path = self._main_prefix + KOBO_ROOT_DIR_NAME + '/kepub/' + path # print "Internal: " + path else: # if path.startswith("file:///mnt/onboard/"): - path = path.replace("file:///mnt/onboard/", self._main_prefix) - path = path.replace("/mnt/onboard/", self._main_prefix) + path = path.replace('file:///mnt/onboard/', self._main_prefix) + path = path.replace('/mnt/onboard/', self._main_prefix) # print "Internal: " + path return path @@ -743,7 +743,7 @@ class KOBO(USBMS): debug_print('The database has been upgraded past supported version') self.report_progress(1.0, _('Removing books from device...')) from calibre.devices.errors import UserFeedback - raise UserFeedback(_("Kobo database version unsupported - See details"), + raise UserFeedback(_('Kobo database version unsupported - See details'), _('Your Kobo is running an updated firmware/database version.' ' As calibre does not know about this updated firmware,' ' database editing is disabled, to prevent corruption.' @@ -773,7 +773,7 @@ class KOBO(USBMS): extension = os.path.splitext(tpath)[1] if extension == '.kobo': from calibre.devices.errors import UserFeedback - raise UserFeedback(_("Not Implemented"), + raise UserFeedback(_('Not Implemented'), _('".kobo" files do not exist on the device as books; ' 'instead they are rows in the sqlite database. ' 'Currently they cannot be exported or viewed.'), @@ -817,9 +817,9 @@ class KOBO(USBMS): # Reset Im_Reading list in the database if oncard == 'carda': - query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ContentID like \'file:///mnt/sd/%\'' + query= "update content set ReadStatus=0, FirstTimeReading = 'true' where BookID is Null and ContentID like 'file:///mnt/sd/%'" elif oncard != 'carda' and oncard != 'cardb': - query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ContentID not like \'file:///mnt/sd/%\'' + query= "update content set ReadStatus=0, FirstTimeReading = 'true' where BookID is Null and ContentID not like 'file:///mnt/sd/%'" try: cursor.execute(query) @@ -830,7 +830,7 @@ class KOBO(USBMS): cursor.close() def set_readstatus(self, connection, ContentID, ReadStatus): - debug_print("Kobo::set_readstatus - ContentID=%s, ReadStatus=%d" % (ContentID, ReadStatus)) + debug_print('Kobo::set_readstatus - ContentID=%s, ReadStatus=%d' % (ContentID, ReadStatus)) cursor = connection.cursor() t = (ContentID,) cursor.execute('select DateLastRead, ReadStatus from Content where BookID is Null and ContentID = ?', t) @@ -851,8 +851,8 @@ class KOBO(USBMS): t = (ReadStatus, datelastread, ContentID,) try: - debug_print("Kobo::set_readstatus - Making change - ContentID=%s, ReadStatus=%d, DateLastRead=%s" % (ContentID, ReadStatus, datelastread)) - cursor.execute('update content set ReadStatus=?,FirstTimeReading=\'false\',DateLastRead=? where BookID is Null and ContentID = ?', t) + debug_print('Kobo::set_readstatus - Making change - ContentID=%s, ReadStatus=%d, DateLastRead=%s' % (ContentID, ReadStatus, datelastread)) + cursor.execute("update content set ReadStatus=?,FirstTimeReading='false',DateLastRead=? where BookID is Null and ContentID = ?", t) except: debug_print(' Database Exception: Unable to update ReadStatus') raise @@ -862,9 +862,9 @@ class KOBO(USBMS): def reset_favouritesindex(self, connection, oncard): # Reset FavouritesIndex list in the database if oncard == 'carda': - query= 'update content set FavouritesIndex=-1 where BookID is Null and ContentID like \'file:///mnt/sd/%\'' + query= "update content set FavouritesIndex=-1 where BookID is Null and ContentID like 'file:///mnt/sd/%'" elif oncard != 'carda' and oncard != 'cardb': - query= 'update content set FavouritesIndex=-1 where BookID is Null and ContentID not like \'file:///mnt/sd/%\'' + query= "update content set FavouritesIndex=-1 where BookID is Null and ContentID not like 'file:///mnt/sd/%'" cursor = connection.cursor() try: @@ -892,28 +892,28 @@ class KOBO(USBMS): def update_device_database_collections(self, booklists, collections_attributes, oncard): debug_print("Kobo:update_device_database_collections - oncard='%s'"%oncard) - if self.modify_database_check("update_device_database_collections") is False: + if self.modify_database_check('update_device_database_collections') is False: return # Only process categories in this list supportedcategories = { - "Im_Reading":1, - "Read":2, - "Closed":3, - "Shortlist":4, + 'Im_Reading':1, + 'Read':2, + 'Closed':3, + 'Shortlist':4, # "Preview":99, # Unsupported as we don't want to change it } # Define lists for the ReadStatus readstatuslist = { - "Im_Reading":1, - "Read":2, - "Closed":3, + 'Im_Reading':1, + 'Read':2, + 'Closed':3, } accessibilitylist = { - "Preview":6, - "Recommendation":4, + 'Preview':6, + 'Recommendation':4, } # debug_print('Starting update_device_database_collections', collections_attributes) @@ -964,10 +964,10 @@ class KOBO(USBMS): pass else: # No collections # Since no collections exist the ReadStatus needs to be reset to 0 (Unread) - debug_print("No Collections - resetting ReadStatus") + debug_print('No Collections - resetting ReadStatus') self.reset_readstatus(connection, oncard) if self.dbversion >= 14: - debug_print("No Collections - resetting FavouritesIndex") + debug_print('No Collections - resetting FavouritesIndex') self.reset_favouritesindex(connection, oncard) # debug_print('Finished update_device_database_collections', collections_attributes) @@ -1076,7 +1076,7 @@ class KOBO(USBMS): # debug_print("ImageId: ", result[0]) ImageID = result[0] except StopIteration: - debug_print("No rows exist in the database - cannot upload") + debug_print('No rows exist in the database - cannot upload') return finally: cursor.close() @@ -1111,7 +1111,7 @@ class KOBO(USBMS): fsync(f) else: - debug_print("ImageID could not be retrieved from the database") + debug_print('ImageID could not be retrieved from the database') def prepare_addable_books(self, paths): ''' @@ -1234,7 +1234,7 @@ class KOBO(USBMS): extension = os.path.splitext(path_map[book_id])[1] ContentType = self.get_content_type_from_extension(extension) if extension else self.get_content_type_from_path(path_map[book_id]) ContentID = self.contentid_from_path(path_map[book_id], ContentType) - debug_print("get_annotations - ContentID: ", ContentID, "ContentType: ", ContentType) + debug_print('get_annotations - ContentID: ', ContentID, 'ContentType: ', ContentType) bookmark_ext = extension @@ -1252,21 +1252,21 @@ class KOBO(USBMS): # last_read_location = bookmark.last_read_location # timestamp = bookmark.timestamp percent_read = bookmark.percent_read - debug_print("Kobo::generate_annotation_html - last_read: ", bookmark.last_read) + debug_print('Kobo::generate_annotation_html - last_read: ', bookmark.last_read) if bookmark.last_read is not None: try: - last_read = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(calendar.timegm(time.strptime(bookmark.last_read, "%Y-%m-%dT%H:%M:%S")))) + last_read = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(calendar.timegm(time.strptime(bookmark.last_read, '%Y-%m-%dT%H:%M:%S')))) except: try: - last_read = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(calendar.timegm(time.strptime(bookmark.last_read, "%Y-%m-%dT%H:%M:%S.%f")))) + last_read = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(calendar.timegm(time.strptime(bookmark.last_read, '%Y-%m-%dT%H:%M:%S.%f')))) except: try: - last_read = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(calendar.timegm(time.strptime(bookmark.last_read, "%Y-%m-%dT%H:%M:%SZ")))) + last_read = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(calendar.timegm(time.strptime(bookmark.last_read, '%Y-%m-%dT%H:%M:%SZ')))) except: - last_read = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime()) + last_read = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime()) else: # self.datetime = time.gmtime() - last_read = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime()) + last_read = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime()) # debug_print("Percent read: ", percent_read) ka_soup = BeautifulSoup() @@ -1276,12 +1276,12 @@ class KOBO(USBMS): # Add the last-read location if bookmark.book_format == 'epub': - markup = _("
Book last read: %(time)s
Percentage read: %(pr)d%%
") % dict( + markup = _('
Book last read: %(time)s
Percentage read: %(pr)d%%
') % dict( time=last_read, # loc=last_read_location, pr=percent_read) else: - markup = _("
Book last read: %(time)s
Percentage read: %(pr)d%%
") % dict( + markup = _('
Book last read: %(time)s
Percentage read: %(pr)d%%
') % dict( time=last_read, # loc=last_read_location, pr=percent_read) @@ -1307,7 +1307,7 @@ class KOBO(USBMS): typ=user_notes[location]['type'], chapter_title=user_notes[location]['chapter_title'], chapter_progress=user_notes[location]['chapter_progress'], - annotation=user_notes[location]['annotation'] if user_notes[location]['annotation'] is not None else "")) + annotation=user_notes[location]['annotation'] if user_notes[location]['annotation'] is not None else '')) elif user_notes[location]['type'] == 'Highlight': annotations.append( _('Chapter %(chapter)d: %(chapter_title)s
%(typ)s
' @@ -1358,7 +1358,7 @@ class KOBO(USBMS): if bm.type == 'kobo_bookmark' and bm.value.last_read: mi = db.get_metadata(db_id, index_is_id=True) - debug_print("KOBO:add_annotation_to_library - Title: ", mi.title) + debug_print('KOBO:add_annotation_to_library - Title: ', mi.title) user_notes_soup = self.generate_annotation_html(bm.value) if mi.comments: a_offset = mi.comments.find('
') @@ -1465,7 +1465,7 @@ class KOBOTOUCH(KOBO): opts = None - TIMESTAMP_STRING = "%Y-%m-%dT%H:%M:%SZ" + TIMESTAMP_STRING = '%Y-%m-%dT%H:%M:%SZ' AURA_PRODUCT_ID = [0x4203] AURA_EDITION2_PRODUCT_ID = [0x4226] @@ -1693,7 +1693,7 @@ class KOBOTOUCH(KOBO): return dummy_bl elif oncard and oncard != 'carda' and oncard != 'cardb': self.report_progress(1.0, _('Getting list of books on device...')) - debug_print("KoboTouch:books - unknown card") + debug_print('KoboTouch:books - unknown card') return dummy_bl prefix = self._card_a_prefix if oncard == 'carda' else \ @@ -1714,15 +1714,15 @@ class KOBOTOUCH(KOBO): bl = self.booklist_class(oncard, prefix, self.settings) opts = self.settings() - debug_print("KoboTouch:books - opts.extra_customization=", opts.extra_customization) - debug_print("KoboTouch:books - driver options=", self) + debug_print('KoboTouch:books - opts.extra_customization=', opts.extra_customization) + debug_print('KoboTouch:books - driver options=', self) debug_print("KoboTouch:books - prefs['manage_device_metadata']=", prefs['manage_device_metadata']) debugging_title = self.debugging_title debug_print("KoboTouch:books - set_debugging_title to '%s'" % debugging_title) bl.set_debugging_title(debugging_title) - debug_print("KoboTouch:books - length bl=%d"%len(bl)) + debug_print('KoboTouch:books - length bl=%d'%len(bl)) need_sync = self.parse_metadata_cache(bl, prefix, self.METADATA_CACHE) - debug_print("KoboTouch:books - length bl after sync=%d"%len(bl)) + debug_print('KoboTouch:books - length bl after sync=%d'%len(bl)) # make a dict cache of paths so the lookup in the loop below is faster. bl_cache = {} @@ -1739,9 +1739,9 @@ class KOBOTOUCH(KOBO): show_debug = self.is_debugging_title(title) # show_debug = authors == 'L. Frank Baum' if show_debug: - debug_print("KoboTouch:update_booklist - title='%s'"%title, "ContentType=%s"%ContentType, "isdownloaded=", isdownloaded) + debug_print("KoboTouch:update_booklist - title='%s'"%title, 'ContentType=%s'%ContentType, 'isdownloaded=', isdownloaded) debug_print( - " prefix=%s, DateCreated=%s, readstatus=%d, MimeType=%s, expired=%d, favouritesindex=%d, accessibility=%d, isdownloaded=%s"% + ' prefix=%s, DateCreated=%s, readstatus=%d, MimeType=%s, expired=%d, favouritesindex=%d, accessibility=%d, isdownloaded=%s'% (prefix, DateCreated, readstatus, MimeType, expired, favouritesindex, accessibility, isdownloaded,)) changed = False try: @@ -1786,7 +1786,7 @@ class KOBOTOUCH(KOBO): playlist_map[lpath].append('Deleted') allow_shelves = False if show_debug: - debug_print("KoboTouch:update_booklist - have a deleted book") + debug_print('KoboTouch:update_booklist - have a deleted book') elif self.supports_kobo_archive() and (accessibility == 1 or accessibility == 2): playlist_map[lpath].append('Archived') allow_shelves = True @@ -1823,7 +1823,7 @@ class KOBOTOUCH(KOBO): # print "Normalized FileName: " + path # Collect the Kobo metadata - authors_list = [a.strip() for a in authors.split("&")] if authors is not None else [_('Unknown')] + authors_list = [a.strip() for a in authors.split('&')] if authors is not None else [_('Unknown')] kobo_metadata = Metadata(title, authors_list) kobo_metadata.series = series kobo_metadata.series_index = seriesnumber @@ -1836,7 +1836,7 @@ class KOBOTOUCH(KOBO): kobo_metadata.pubdate = parse_date(DateCreated, assume_utc=True) except: try: - kobo_metadata.pubdate = datetime.strptime(DateCreated, "%Y-%m-%dT%H:%M:%S.%fZ") + kobo_metadata.pubdate = datetime.strptime(DateCreated, '%Y-%m-%dT%H:%M:%S.%fZ') except: debug_print("KoboTouch:update_booklist - Cannot convert date - DateCreated='%s'"%DateCreated) @@ -1844,8 +1844,8 @@ class KOBOTOUCH(KOBO): if idx is not None: # and not (accessibility == 1 and isdownloaded == 'false'): if show_debug: self.debug_index = idx - debug_print("KoboTouch:update_booklist - idx=%d"%idx) - debug_print("KoboTouch:update_booklist - lpath=%s"%lpath) + debug_print('KoboTouch:update_booklist - idx=%d'%idx) + debug_print('KoboTouch:update_booklist - lpath=%s'%lpath) debug_print('KoboTouch:update_booklist - bl[idx].device_collections=', bl[idx].device_collections) debug_print('KoboTouch:update_booklist - playlist_map=', playlist_map) debug_print('KoboTouch:update_booklist - bookshelves=', bookshelves) @@ -1867,8 +1867,8 @@ class KOBOTOUCH(KOBO): # debug_print("KoboTouch:update_booklist - update_metadata_item returned true") changed = True else: - debug_print(" Strange: The file: ", prefix, lpath, " does not exist!") - debug_print("KoboTouch:update_booklist - book size=", bl[idx].size) + debug_print(' Strange: The file: ', prefix, lpath, ' does not exist!') + debug_print('KoboTouch:update_booklist - book size=', bl[idx].size) if show_debug: debug_print("KoboTouch:update_booklist - ContentID='%s'"%ContentID) @@ -1897,8 +1897,8 @@ class KOBOTOUCH(KOBO): debug_print('KoboTouch:update_booklist - updated bl[idx].device_collections=', bl[idx].device_collections) debug_print('KoboTouch:update_booklist - playlist_map=', playlist_map, 'changed=', changed) # debug_print('KoboTouch:update_booklist - book=', bl[idx]) - debug_print("KoboTouch:update_booklist - book class=%s"%bl[idx].__class__) - debug_print("KoboTouch:update_booklist - book title=%s"%bl[idx].title) + debug_print('KoboTouch:update_booklist - book class=%s'%bl[idx].__class__) + debug_print('KoboTouch:update_booklist - book title=%s'%bl[idx].title) else: if show_debug: debug_print('KoboTouch:update_booklist - idx is none') @@ -1907,16 +1907,16 @@ class KOBOTOUCH(KOBO): book = self.book_from_path(prefix, lpath, title, authors, MimeType, DateCreated, ContentType, ImageID) else: if isdownloaded == 'true': # A recommendation or preview is OK to not have a file - debug_print(" Strange: The file: ", prefix, lpath, " does not exist!") - title = "FILE MISSING: " + title + debug_print(' Strange: The file: ', prefix, lpath, ' does not exist!') + title = 'FILE MISSING: ' + title book = self.book_class(prefix, lpath, title, authors, MimeType, DateCreated, ContentType, ImageID, size=0) if show_debug: debug_print('KoboTouch:update_booklist - book file does not exist. ContentID="%s"'%ContentID) except Exception as e: debug_print("KoboTouch:update_booklist - exception creating book: '%s'"%str(e)) - debug_print(" prefix: ", prefix, "lpath: ", lpath, "title: ", title, "authors: ", authors, - "MimeType: ", MimeType, "DateCreated: ", DateCreated, "ContentType: ", ContentType, "ImageID: ", ImageID) + debug_print(' prefix: ', prefix, 'lpath: ', lpath, 'title: ', title, 'authors: ', authors, + 'MimeType: ', MimeType, 'DateCreated: ', DateCreated, 'ContentType: ', ContentType, 'ImageID: ', ImageID) raise if show_debug: @@ -1924,10 +1924,10 @@ class KOBOTOUCH(KOBO): # debug_print(' resolution:', book.__class__.__mro__) debug_print(" contentid: '%s'"%book.contentID) debug_print(" title:'%s'"%book.title) - debug_print(" the book:", book) + debug_print(' the book:', book) debug_print(" author_sort:'%s'"%book.author_sort) - debug_print(" bookshelves:", bookshelves) - debug_print(" kobo_collections:", kobo_collections) + debug_print(' bookshelves:', bookshelves) + debug_print(' kobo_collections:', kobo_collections) # print 'Update booklist' book.device_collections = playlist_map.get(lpath,[]) # if lpath in playlist_map else [] @@ -1966,11 +1966,11 @@ class KOBOTOUCH(KOBO): return bookshelves cursor = connection.cursor() - query = "select ShelfName " \ - "from ShelfContent " \ - "where ContentId = ? " \ - f"and _IsDeleted = {self.bool_for_query(False)} " \ - "and ShelfName is not null" # This should never be null, but it is protection against an error cause by a sync to the Kobo server + query = 'select ShelfName ' \ + 'from ShelfContent ' \ + 'where ContentId = ? ' \ + f'and _IsDeleted = {self.bool_for_query(False)} ' \ + 'and ShelfName is not null' # This should never be null, but it is protection against an error cause by a sync to the Kobo server values = (ContentID, ) cursor.execute(query, values) for i, row in enumerate(cursor): @@ -1983,13 +1983,13 @@ class KOBOTOUCH(KOBO): self.debug_index = 0 with closing(self.device_database_connection(use_row_factory=True)) as connection: - debug_print("KoboTouch:books - reading device database") + debug_print('KoboTouch:books - reading device database') self.dbversion = self.get_database_version(connection) - debug_print("Database Version: ", self.dbversion) + debug_print('Database Version: ', self.dbversion) self.bookshelvelist = self.get_bookshelflist(connection) - debug_print("KoboTouch:books - shelf list:", self.bookshelvelist) + debug_print('KoboTouch:books - shelf list:', self.bookshelvelist) columns = 'Title, Attribution, DateCreated, ContentID, MimeType, ContentType, ImageId, ReadStatus, Description, Publisher ' if self.dbversion >= 16: @@ -2005,15 +2005,15 @@ class KOBOTOUCH(KOBO): else: columns += ', NULL AS ISBN' if self.supports_series(): - columns += ", Series, SeriesNumber, ___UserID, ExternalId, Subtitle" + columns += ', Series, SeriesNumber, ___UserID, ExternalId, Subtitle' else: columns += ', null as Series, null as SeriesNumber, ___UserID, null as ExternalId, null as Subtitle' if self.supports_series_list: - columns += ", SeriesID, SeriesNumberFloat" + columns += ', SeriesID, SeriesNumberFloat' else: columns += ', null as SeriesID, null as SeriesNumberFloat' if self.supports_bookstats: - columns += ", StorePages, StoreWordCount, StoreTimeToReadLowerEstimate, StoreTimeToReadUpperEstimate" + columns += ', StorePages, StoreWordCount, StoreTimeToReadLowerEstimate, StoreTimeToReadUpperEstimate' else: columns += ', null as StorePages, null as StoreWordCount, null as StoreTimeToReadLowerEstimate, null as StoreTimeToReadUpperEstimate' @@ -2025,10 +2025,10 @@ class KOBOTOUCH(KOBO): " %(previews)s %(recommendations)s ) " # Previews or Recommendations ) % \ dict( - expiry="" if self.show_archived_books else "and IsDownloaded in ('true', 1)", - previews=" OR (Accessibility in (6) AND ___UserID <> '')" if self.show_previews else "", - recommendations=" OR (Accessibility IN (-1, 4, 6) AND ___UserId = '')" if self.show_recommendations else "", - downloaded_accessibility="1,2,8,9" if self.supports_overdrive() else "1,2" + expiry='' if self.show_archived_books else "and IsDownloaded in ('true', 1)", + previews=" OR (Accessibility in (6) AND ___UserID <> '')" if self.show_previews else '', + recommendations=" OR (Accessibility IN (-1, 4, 6) AND ___UserId = '')" if self.show_recommendations else '', + downloaded_accessibility='1,2,8,9' if self.supports_overdrive() else '1,2' ) elif self.supports_series(): where_clause = (" WHERE BookID IS NULL " @@ -2036,9 +2036,9 @@ class KOBOTOUCH(KOBO): " AND NOT ((___ExpirationStatus=3 OR ___ExpirationStatus is Null) %(expiry)s)" ) % \ dict( - expiry=" AND ContentType = 6" if self.show_archived_books else "", - previews=" or (Accessibility IN (6) AND ___UserID <> '')" if self.show_previews else "", - recommendations=" or (Accessibility in (-1, 4, 6) AND ___UserId = '')" if self.show_recommendations else "" + expiry=' AND ContentType = 6' if self.show_archived_books else '', + previews=" or (Accessibility IN (6) AND ___UserID <> '')" if self.show_previews else '', + recommendations=" or (Accessibility in (-1, 4, 6) AND ___UserId = '')" if self.show_recommendations else '' ) elif self.dbversion >= 33: where_clause = (' WHERE BookID IS NULL %(previews)s %(recommendations)s AND NOT' @@ -2047,7 +2047,7 @@ class KOBOTOUCH(KOBO): dict( expiry=' AND ContentType = 6' if self.show_archived_books else '', previews=' AND Accessibility <> 6' if not self.show_previews else '', - recommendations=' AND IsDownloaded IN (\'true\', 1)' if not self.show_recommendations else '' + recommendations=" AND IsDownloaded IN ('true', 1)" if not self.show_recommendations else '' ) elif self.dbversion >= 16: where_clause = (' WHERE BookID IS NULL ' @@ -2068,7 +2068,7 @@ class KOBOTOUCH(KOBO): card_condition = " AND contentId LIKE 'file:///mnt/sd/%'" if oncard == 'carda' else " AND contentId NOT LIKE'file:///mnt/sd/%'" query = 'SELECT ' + columns + ' FROM content ' + where_clause + card_condition - debug_print("KoboTouch:books - query=", query) + debug_print('KoboTouch:books - query=', query) cursor = connection.cursor() try: @@ -2093,17 +2093,17 @@ class KOBOTOUCH(KOBO): # self.report_progress((i) / float(books_on_device), _('Getting list of books on device...')) show_debug = self.is_debugging_title(row['Title']) if show_debug: - debug_print("KoboTouch:books - looping on database - row=%d" % i) - debug_print("KoboTouch:books - title='%s'"%row['Title'], "authors=", row['Attribution']) - debug_print("KoboTouch:books - row=", row) + debug_print('KoboTouch:books - looping on database - row=%d' % i) + debug_print("KoboTouch:books - title='%s'"%row['Title'], 'authors=', row['Attribution']) + debug_print('KoboTouch:books - row=', row) if not hasattr(row['ContentID'], 'startswith') or row['ContentID'].lower().startswith( - "file:///usr/local/kobo/help/") or row['ContentID'].lower().startswith("/usr/local/kobo/help/"): + 'file:///usr/local/kobo/help/') or row['ContentID'].lower().startswith('/usr/local/kobo/help/'): # These are internal to the Kobo device and do not exist continue externalId = None if row['ExternalId'] and len(row['ExternalId']) == 0 else row['ExternalId'] path = self.path_from_contentid(row['ContentID'], row['ContentType'], row['MimeType'], oncard, externalId) if show_debug: - debug_print("KoboTouch:books - path='%s'"%path, " ContentID='%s'"%row['ContentID'], " externalId=%s" % externalId) + debug_print("KoboTouch:books - path='%s'"%path, " ContentID='%s'"%row['ContentID'], ' externalId=%s' % externalId) bookshelves = get_bookshelvesforbook(connection, row['ContentID']) @@ -2131,8 +2131,8 @@ class KOBOTOUCH(KOBO): if not prefs['manage_device_metadata'] == 'on_connect': self.dump_bookshelves(connection) else: - debug_print("KoboTouch:books - automatically managing metadata") - debug_print("KoboTouch:books - self.kobo_series_dict=", self.kobo_series_dict) + debug_print('KoboTouch:books - automatically managing metadata') + debug_print('KoboTouch:books - self.kobo_series_dict=', self.kobo_series_dict) # Remove books that are no longer in the filesystem. Cache contains # indices into the booklist if book not in filesystem, None otherwise # Do the operation in reverse order so indices remain valid @@ -2149,14 +2149,14 @@ class KOBOTOUCH(KOBO): # Bypassing the KOBO sync_booklists as that does things we don't need to do # Also forcing sync to see if this solves issues with updating shelves and matching books. if need_sync or True: # self.count_found_in_bl != len(bl) or need_sync: - debug_print("KoboTouch:books - about to sync_booklists") + debug_print('KoboTouch:books - about to sync_booklists') if oncard == 'cardb': USBMS.sync_booklists(self, (None, None, bl)) elif oncard == 'carda': USBMS.sync_booklists(self, (None, bl, None)) else: USBMS.sync_booklists(self, (bl, None, None)) - debug_print("KoboTouch:books - have done sync_booklists") + debug_print('KoboTouch:books - have done sync_booklists') self.report_progress(1.0, _('Getting list of books on device...')) debug_print("KoboTouch:books - end - oncard='%s'"%oncard) @@ -2164,7 +2164,7 @@ class KOBOTOUCH(KOBO): @classmethod def book_from_path(cls, prefix, lpath, title, authors, mime, date, ContentType, ImageID): - debug_print("KoboTouch:book_from_path - title=%s"%title) + debug_print('KoboTouch:book_from_path - title=%s'%title) book = super().book_from_path(prefix, lpath, title, authors, mime, date, ContentType, ImageID) # Kobo Audiobooks are directories with files in them. @@ -2177,7 +2177,7 @@ class KOBOTOUCH(KOBO): size = audiofile.stat().st_size # debug_print("KoboTouch:book_from_path - size=", size) book.size += size - debug_print("KoboTouch:book_from_path - book.size=", book.size) + debug_print('KoboTouch:book_from_path - book.size=', book.size) return book @@ -2190,24 +2190,24 @@ class KOBOTOUCH(KOBO): if oncard == 'cardb': print('path from_contentid cardb') else: - if (ContentType == "6" or ContentType == "10"): + if (ContentType == '6' or ContentType == '10'): if (MimeType == 'application/octet-stream'): # Audiobooks purchased from Kobo are in a different location. path = self._main_prefix + KOBO_ROOT_DIR_NAME + '/audiobook/' + path elif (MimeType == 'audio/mpeg' and self.isTolinoDevice()): path = self._main_prefix + KOBO_ROOT_DIR_NAME + '/audiobook/' + path - elif path.startswith("file:///mnt/onboard/"): - path = self._main_prefix + path.replace("file:///mnt/onboard/", '') - elif path.startswith("file:///mnt/sd/"): - path = self._card_a_prefix + path.replace("file:///mnt/sd/", '') + elif path.startswith('file:///mnt/onboard/'): + path = self._main_prefix + path.replace('file:///mnt/onboard/', '') + elif path.startswith('file:///mnt/sd/'): + path = self._card_a_prefix + path.replace('file:///mnt/sd/', '') elif externalId: path = self._card_a_prefix + 'koboExtStorage/kepub/' + path else: path = self._main_prefix + KOBO_ROOT_DIR_NAME + '/kepub/' + path else: # Should never get here, but, just in case... # if path.startswith("file:///mnt/onboard/"): - path = path.replace("file:///mnt/onboard/", self._main_prefix) - path = path.replace("file:///mnt/sd/", self._card_a_prefix) - path = path.replace("/mnt/onboard/", self._main_prefix) + path = path.replace('file:///mnt/onboard/', self._main_prefix) + path = path.replace('file:///mnt/sd/', self._card_a_prefix) + path = path.replace('/mnt/onboard/', self._main_prefix) # print "Internal: " + path return path @@ -2222,11 +2222,11 @@ class KOBOTOUCH(KOBO): fpath = path + ending if os.path.exists(fpath): if show_debug: - debug_print("KoboTouch:imagefilename_from_imageID - have cover image fpath=%s" % (fpath)) + debug_print('KoboTouch:imagefilename_from_imageID - have cover image fpath=%s' % (fpath)) return fpath if show_debug: - debug_print("KoboTouch:imagefilename_from_imageID - no cover image found - ImageID=%s" % (ImageID)) + debug_print('KoboTouch:imagefilename_from_imageID - no cover image found - ImageID=%s' % (ImageID)) return None def get_extra_css(self): @@ -2239,13 +2239,13 @@ class KOBOTOUCH(KOBO): from css_parser import parseFile as cssparseFile try: extra_sheet = cssparseFile(extra_css_path) - debug_print(f"KoboTouch:get_extra_css: Using extra CSS in {extra_css_path} ({len(extra_sheet.cssRules)} rules)") + debug_print(f'KoboTouch:get_extra_css: Using extra CSS in {extra_css_path} ({len(extra_sheet.cssRules)} rules)') if len(extra_sheet.cssRules) ==0: - debug_print("KoboTouch:get_extra_css: Extra CSS file has no valid rules. CSS will not be modified.") + debug_print('KoboTouch:get_extra_css: Extra CSS file has no valid rules. CSS will not be modified.') extra_sheet = None except Exception as e: - debug_print(f"KoboTouch:get_extra_css: Problem parsing extra CSS file {extra_css_path}") - debug_print(f"KoboTouch:get_extra_css: Exception {e}") + debug_print(f'KoboTouch:get_extra_css: Problem parsing extra CSS file {extra_css_path}') + debug_print(f'KoboTouch:get_extra_css: Exception {e}') # create dictionary of features enabled in kobo extra css self.extra_css_options = {} @@ -2276,9 +2276,9 @@ class KOBOTOUCH(KOBO): self.extra_sheet = self.get_extra_css() i = 0 for file, n, mi in zip(files, names, metadata): - debug_print("KoboTouch:upload_books: Processing book: {} by {}".format(mi.title, " and ".join(mi.authors))) - debug_print(f"KoboTouch:upload_books: file={file}, name={n}") - self.report_progress(i / float(len(files)), "Processing book: {} by {}".format(mi.title, " and ".join(mi.authors))) + debug_print('KoboTouch:upload_books: Processing book: {} by {}'.format(mi.title, ' and '.join(mi.authors))) + debug_print(f'KoboTouch:upload_books: file={file}, name={n}') + self.report_progress(i / float(len(files)), 'Processing book: {} by {}'.format(mi.title, ' and '.join(mi.authors))) mi.kte_calibre_name = n self._modify_epub(file, mi) i += 1 @@ -2292,7 +2292,7 @@ class KOBOTOUCH(KOBO): try: with closing(self.device_database_connection()) as connection: cursor = connection.cursor() - cleanup_query = f"DELETE FROM content WHERE ContentID = ? AND Accessibility = 1 AND IsDownloaded = {self.bool_for_query(False)}" + cleanup_query = f'DELETE FROM content WHERE ContentID = ? AND Accessibility = 1 AND IsDownloaded = {self.bool_for_query(False)}' for fname, cycle in result: show_debug = self.is_debugging_title(fname) contentID = self.contentid_from_path(fname, 6) @@ -2318,11 +2318,11 @@ class KOBOTOUCH(KOBO): return result def _modify_epub(self, book_file, metadata, container=None): - debug_print(f"KoboTouch:_modify_epub:Processing {metadata.author_sort} - {metadata.title}") + debug_print(f'KoboTouch:_modify_epub:Processing {metadata.author_sort} - {metadata.title}') # Currently only modifying CSS, so if no stylesheet, don't do anything if not self.extra_sheet: - debug_print("KoboTouch:_modify_epub: no CSS file") + debug_print('KoboTouch:_modify_epub: no CSS file') return True container, commit_container = self.create_container(book_file, metadata, container) @@ -2339,14 +2339,14 @@ class KOBOTOUCH(KOBO): # future css mods may be epub/kepub specific, so pass file extension arg fileext = os.path.splitext(book_file)[-1].lower() - debug_print(f"KoboTouch:_modify_epub: Modifying {cssname}") + debug_print(f'KoboTouch:_modify_epub: Modifying {cssname}') if self._modify_stylesheet(newsheet, fileext): - debug_print(f"KoboTouch:_modify_epub:CSS rules {oldrules} -> {len(newsheet.cssRules)} ({cssname})") + debug_print(f'KoboTouch:_modify_epub:CSS rules {oldrules} -> {len(newsheet.cssRules)} ({cssname})') container.dirty(cssname) is_dirty = True if commit_container: - debug_print("KoboTouch:_modify_epub: committing container.") + debug_print('KoboTouch:_modify_epub: committing container.') self.commit_container(container, is_dirty) return True @@ -2361,7 +2361,7 @@ class KOBOTOUCH(KOBO): if self.extra_css_options.get('has_atpage', False): page_rules = self.get_extra_css_rules(sheet, CSSRule.PAGE_RULE) if len(page_rules) > 0: - debug_print("KoboTouch:_modify_stylesheet: Removing existing @page rules") + debug_print('KoboTouch:_modify_stylesheet: Removing existing @page rules') for rule in page_rules: rule.style = '' is_dirty = True @@ -2371,14 +2371,14 @@ class KOBOTOUCH(KOBO): if self.extra_css_options.get('has_widows_orphans', False): widow_orphan_rules = self.get_extra_css_rules_widow_orphan(sheet) if len(widow_orphan_rules) > 0: - debug_print("KoboTouch:_modify_stylesheet: Removing existing widows/orphans attribs") + debug_print('KoboTouch:_modify_stylesheet: Removing existing widows/orphans attribs') for rule in widow_orphan_rules: rule.style.removeProperty('widows') rule.style.removeProperty('orphans') is_dirty = True # append all rules from kobo extra css - debug_print("KoboTouch:_modify_stylesheet: Append all kobo extra css rules") + debug_print('KoboTouch:_modify_stylesheet: Append all kobo extra css rules') for extra_rule in self.extra_sheet.cssRules: sheet.insertRule(extra_rule) is_dirty = True @@ -2391,25 +2391,25 @@ class KOBOTOUCH(KOBO): commit_container = True try: from calibre.ebooks.oeb.polish.container import get_container - debug_print("KoboTouch:create_container: try to create new container") + debug_print('KoboTouch:create_container: try to create new container') container = get_container(book_file) container.css_preprocessor = DummyCSSPreProcessor() except Exception as e: - debug_print(f"KoboTouch:create_container: exception from get_container {metadata.author_sort} - {metadata.title}") - debug_print(f"KoboTouch:create_container: exception is: {e}") + debug_print(f'KoboTouch:create_container: exception from get_container {metadata.author_sort} - {metadata.title}') + debug_print(f'KoboTouch:create_container: exception is: {e}') else: commit_container = False - debug_print("KoboTouch:create_container: received container") + debug_print('KoboTouch:create_container: received container') return container, commit_container def commit_container(self, container, is_dirty=True): # commit container if changes have been made if is_dirty: - debug_print("KoboTouch:commit_container: commit container.") + debug_print('KoboTouch:commit_container: commit container.') container.commit() # Clean-up-AYGO prevents build-up of TEMP exploded epub/kepub files - debug_print("KoboTouch:commit_container: removing container temp files.") + debug_print('KoboTouch:commit_container: removing container temp files.') try: shutil.rmtree(container.root) except Exception: @@ -2466,18 +2466,18 @@ class KOBOTOUCH(KOBO): return imageId def delete_images(self, ImageID, book_path): - debug_print("KoboTouch:delete_images - ImageID=", ImageID) + debug_print('KoboTouch:delete_images - ImageID=', ImageID) if ImageID is not None: path = self.images_path(book_path, ImageID) - debug_print("KoboTouch:delete_images - path=%s" % path) + debug_print('KoboTouch:delete_images - path=%s' % path) for ending in self.cover_file_endings().keys(): fpath = path + ending fpath = self.normalize_path(fpath) - debug_print("KoboTouch:delete_images - fpath=%s" % fpath) + debug_print('KoboTouch:delete_images - fpath=%s' % fpath) if os.path.exists(fpath): - debug_print("KoboTouch:delete_images - Image File Exists") + debug_print('KoboTouch:delete_images - Image File Exists') os.unlink(fpath) try: @@ -2501,20 +2501,20 @@ class KOBOTOUCH(KOBO): ContentID = ContentID.replace(self._main_prefix + self.normalize_path(KOBO_ROOT_DIR_NAME + '/kepub/'), '') else: ContentID = path - ContentID = ContentID.replace(self._main_prefix, "file:///mnt/onboard/") + ContentID = ContentID.replace(self._main_prefix, 'file:///mnt/onboard/') if show_debug: debug_print("KoboTouch:contentid_from_path - 1 ContentID='%s'"%ContentID) if self._card_a_prefix is not None: - ContentID = ContentID.replace(self._card_a_prefix, "file:///mnt/sd/") + ContentID = ContentID.replace(self._card_a_prefix, 'file:///mnt/sd/') else: # ContentType = 16 debug_print("KoboTouch:contentid_from_path ContentType other than 6 - ContentType='%d'"%ContentType, "path='%s'"%path) ContentID = path - ContentID = ContentID.replace(self._main_prefix, "file:///mnt/onboard/") + ContentID = ContentID.replace(self._main_prefix, 'file:///mnt/onboard/') if self._card_a_prefix is not None: - ContentID = ContentID.replace(self._card_a_prefix, "file:///mnt/sd/") - ContentID = ContentID.replace("\\", '/') + ContentID = ContentID.replace(self._card_a_prefix, 'file:///mnt/sd/') + ContentID = ContentID.replace('\\', '/') if show_debug: debug_print("KoboTouch:contentid_from_path - end - ContentID='%s'"%ContentID) return ContentID @@ -2526,7 +2526,7 @@ class KOBOTOUCH(KOBO): return ContentType def get_content_type_from_extension(self, extension): - debug_print("KoboTouch:get_content_type_from_extension - start") + debug_print('KoboTouch:get_content_type_from_extension - start') # With new firmware, ContentType appears to be 6 for all types of sideloaded books. ContentType = 6 if self.fwversion < (1,9,17): @@ -2540,30 +2540,30 @@ class KOBOTOUCH(KOBO): def update_device_database_collections(self, booklists, collections_attributes, oncard): debug_print("KoboTouch:update_device_database_collections - oncard='%s'"%oncard) debug_print("KoboTouch:update_device_database_collections - device='%s'" % self) - if self.modify_database_check("update_device_database_collections") is False: + if self.modify_database_check('update_device_database_collections') is False: return # Only process categories in this list supportedcategories = { - "Im_Reading": 1, - "Read": 2, - "Closed": 3, - "Shortlist": 4, - "Archived": 5, + 'Im_Reading': 1, + 'Read': 2, + 'Closed': 3, + 'Shortlist': 4, + 'Archived': 5, } # Define lists for the ReadStatus readstatuslist = { - "Im_Reading":1, - "Read":2, - "Closed":3, + 'Im_Reading':1, + 'Read':2, + 'Closed':3, } accessibilitylist = { - "Deleted":1, - "OverDrive":9, - "Preview":6, - "Recommendation":4, + 'Deleted':1, + 'OverDrive':9, + 'Preview':6, + 'Recommendation':4, } # debug_print('KoboTouch:update_device_database_collections - collections_attributes=', collections_attributes) @@ -2604,10 +2604,10 @@ class KOBOTOUCH(KOBO): # Need to reset the collections outside the particular loops # otherwise the last item will not be removed if self.dbversion < 53: - debug_print("KoboTouch:update_device_database_collections - calling reset_readstatus") + debug_print('KoboTouch:update_device_database_collections - calling reset_readstatus') self.reset_readstatus(connection, oncard) if self.dbversion >= 14 and self.fwversion < self.min_fwversion_shelves: - debug_print("KoboTouch:update_device_database_collections - calling reset_favouritesindex") + debug_print('KoboTouch:update_device_database_collections - calling reset_favouritesindex') self.reset_favouritesindex(connection, oncard) # debug_print("KoboTouch:update_device_database_collections - length collections=", len(collections)) @@ -2681,17 +2681,17 @@ class KOBOTOUCH(KOBO): elif have_bookshelf_attributes: # No collections but have set the shelf option # Since no collections exist the ReadStatus needs to be reset to 0 (Unread) - debug_print("No Collections - resetting ReadStatus") + debug_print('No Collections - resetting ReadStatus') if self.dbversion < 53: self.reset_readstatus(connection, oncard) if self.dbversion >= 14 and self.fwversion < self.min_fwversion_shelves: - debug_print("No Collections - resetting FavouritesIndex") + debug_print('No Collections - resetting FavouritesIndex') self.reset_favouritesindex(connection, oncard) # Set the series info and cleanup the bookshelves only if the firmware supports them and the user has set the options. if (self.supports_bookshelves and self.manage_collections or self.supports_series()) and ( have_bookshelf_attributes or update_series_details or update_core_metadata): - debug_print("KoboTouch:update_device_database_collections - managing bookshelves and series.") + debug_print('KoboTouch:update_device_database_collections - managing bookshelves and series.') self.series_set = 0 self.core_metadata_set = 0 @@ -2702,29 +2702,29 @@ class KOBOTOUCH(KOBO): books_in_library += 1 show_debug = self.is_debugging_title(book.title) if show_debug: - debug_print("KoboTouch:update_device_database_collections - book.title=%s" % book.title) + debug_print('KoboTouch:update_device_database_collections - book.title=%s' % book.title) debug_print( - "KoboTouch:update_device_database_collections - contentId=%s," - "update_core_metadata=%s,update_purchased_kepubs=%s, book.is_sideloaded=%s" % ( + 'KoboTouch:update_device_database_collections - contentId=%s,' + 'update_core_metadata=%s,update_purchased_kepubs=%s, book.is_sideloaded=%s' % ( book.contentID, update_core_metadata, update_purchased_kepubs, book.is_sideloaded)) if update_core_metadata and (update_purchased_kepubs or book.is_sideloaded): if show_debug: - debug_print("KoboTouch:update_device_database_collections - calling set_core_metadata") + debug_print('KoboTouch:update_device_database_collections - calling set_core_metadata') self.set_core_metadata(connection, book) elif update_series_details: if show_debug: - debug_print("KoboTouch:update_device_database_collections - calling set_core_metadata - series only") + debug_print('KoboTouch:update_device_database_collections - calling set_core_metadata - series only') self.set_core_metadata(connection, book, series_only=True) if self.manage_collections and have_bookshelf_attributes: if show_debug: - debug_print("KoboTouch:update_device_database_collections - about to remove a book from shelves book.title=%s" % book.title) + debug_print('KoboTouch:update_device_database_collections - about to remove a book from shelves book.title=%s' % book.title) self.remove_book_from_device_bookshelves(connection, book) book.device_collections.extend(book.kobo_collections) if not prefs['manage_device_metadata'] == 'manual' and delete_empty_collections: - debug_print("KoboTouch:update_device_database_collections - about to clear empty bookshelves") + debug_print('KoboTouch:update_device_database_collections - about to clear empty bookshelves') self.delete_empty_bookshelves(connection) - debug_print("KoboTouch:update_device_database_collections - Number of series set=%d Number of books=%d" % (self.series_set, books_in_library)) - debug_print("KoboTouch:update_device_database_collections - Number of core metadata set=%d Number of books=%d" % ( + debug_print('KoboTouch:update_device_database_collections - Number of series set=%d Number of books=%d' % (self.series_set, books_in_library)) + debug_print('KoboTouch:update_device_database_collections - Number of core metadata set=%d Number of books=%d' % ( self.core_metadata_set, books_in_library)) self.dump_bookshelves(connection) @@ -2732,7 +2732,7 @@ class KOBOTOUCH(KOBO): debug_print('KoboTouch:update_device_database_collections - Finished ') def rebuild_collections(self, booklist, oncard): - debug_print("KoboTouch:rebuild_collections") + debug_print('KoboTouch:rebuild_collections') collections_attributes = self.get_collections_attributes() debug_print('KoboTouch:rebuild_collections: collection fields:', collections_attributes) @@ -2793,7 +2793,7 @@ class KOBOTOUCH(KOBO): hash1 = qhash(imageId) dir1 = hash1 & (0xff * 1) dir2 = (hash1 & (0xff00 * 1)) >> 8 - path = os.path.join(path, "%s" % dir1, "%s" % dir2) + path = os.path.join(path, '%s' % dir1, '%s' % dir2) if imageId: path = os.path.join(path, imageId) @@ -2854,7 +2854,7 @@ class KOBOTOUCH(KOBO): from calibre.utils.img import save_cover_data_to data = save_cover_data_to( cover_data, resize_to=resize_to, compression_quality=quality, minify_to=minify_to, grayscale=upload_grayscale, eink=dithered_covers, - letterbox=letterbox, data_fmt="png" if png_covers else "jpeg", letterbox_color=letterbox_color) + letterbox=letterbox, data_fmt='png' if png_covers else 'jpeg', letterbox_color=letterbox_color) return data def _upload_cover( @@ -2876,7 +2876,7 @@ class KOBOTOUCH(KOBO): cover = self.normalize_path(metadata.cover.replace('/', os.sep)) if not os.path.exists(cover): - debug_print("KoboTouch:_upload_cover - Cover file does not exist in library") + debug_print('KoboTouch:_upload_cover - Cover file does not exist in library') return # Get ContentID for Selected Book @@ -2903,7 +2903,7 @@ class KOBOTOUCH(KOBO): path = self.images_path(path, ImageID) if show_debug: - debug_print("KoboTouch:_upload_cover - About to loop over cover endings") + debug_print('KoboTouch:_upload_cover - About to loop over cover endings') image_dir = os.path.dirname(os.path.abspath(path)) if not os.path.exists(image_dir): @@ -2919,7 +2919,7 @@ class KOBOTOUCH(KOBO): for ending, cover_options in self.cover_file_endings().items(): kobo_size, min_dbversion, max_dbversion, is_full_size = cover_options if show_debug: - debug_print("KoboTouch:_upload_cover - library_cover_size=%s -> kobo_size=%s, min_dbversion=%d max_dbversion=%d, is_full_size=%s" % ( + debug_print('KoboTouch:_upload_cover - library_cover_size=%s -> kobo_size=%s, min_dbversion=%d max_dbversion=%d, is_full_size=%s' % ( library_cover_size, kobo_size, min_dbversion, max_dbversion, is_full_size)) if self.dbversion >= min_dbversion and self.dbversion <= max_dbversion: @@ -2943,8 +2943,8 @@ class KOBOTOUCH(KOBO): resize_to, expand_to = self._calculate_kobo_cover_size(library_cover_size, kobo_size, not is_full_size, keep_cover_aspect, letterbox) if show_debug: debug_print( - "KoboTouch:_calculate_kobo_cover_size - expand_to=%s" - " (vs. kobo_size=%s) & resize_to=%s, keep_cover_aspect=%s & letterbox_fs_covers=%s, png_covers=%s" % ( + 'KoboTouch:_calculate_kobo_cover_size - expand_to=%s' + ' (vs. kobo_size=%s) & resize_to=%s, keep_cover_aspect=%s & letterbox_fs_covers=%s, png_covers=%s' % ( expand_to, kobo_size, resize_to, keep_cover_aspect, letterbox_fs_covers, png_covers)) # NOTE: To speed things up, we enforce a lower @@ -2983,7 +2983,7 @@ class KOBOTOUCH(KOBO): fsync(f) except Exception as e: err = str(e) - debug_print("KoboTouch:_upload_cover - Exception string: %s"%err) + debug_print('KoboTouch:_upload_cover - Exception string: %s'%err) raise def remove_book_from_device_bookshelves(self, connection, book): @@ -3061,7 +3061,7 @@ class KOBOTOUCH(KOBO): # debug_print("KoboTouch:set_filesize_in_device_database - end") def delete_empty_bookshelves(self, connection): - debug_print("KoboTouch:delete_empty_bookshelves - start") + debug_print('KoboTouch:delete_empty_bookshelves - start') ignore_collections_placeholder = '' ignore_collections_values = [] @@ -3069,8 +3069,8 @@ class KOBOTOUCH(KOBO): placeholder = ',?' ignore_collections_placeholder = ''.join(placeholder for unused in self.ignore_collections_names) ignore_collections_values.extend(self.ignore_collections_names) - debug_print("KoboTouch:delete_empty_bookshelves - ignore_collections_in=", ignore_collections_placeholder) - debug_print("KoboTouch:delete_empty_bookshelves - ignore_collections=", ignore_collections_values) + debug_print('KoboTouch:delete_empty_bookshelves - ignore_collections_in=', ignore_collections_placeholder) + debug_print('KoboTouch:delete_empty_bookshelves - ignore_collections=', ignore_collections_values) true, false = self.bool_for_query(True), self.bool_for_query(False) delete_query = ("DELETE FROM Shelf " @@ -3081,7 +3081,7 @@ class KOBOTOUCH(KOBO): "(SELECT 1 FROM ShelfContent c " "WHERE Shelf.Name = c.ShelfName " f"AND c._IsDeleted <> {true})") - debug_print("KoboTouch:delete_empty_bookshelves - delete_query=", delete_query) + debug_print('KoboTouch:delete_empty_bookshelves - delete_query=', delete_query) update_query = ("UPDATE Shelf " f"SET _IsDeleted = {true} " @@ -3092,7 +3092,7 @@ class KOBOTOUCH(KOBO): "(SELECT 1 FROM ShelfContent c " "WHERE Shelf.Name = c.ShelfName " f"AND c._IsDeleted <> {true})") - debug_print("KoboTouch:delete_empty_bookshelves - update_query=", update_query) + debug_print('KoboTouch:delete_empty_bookshelves - update_query=', update_query) delete_activity_query = ("DELETE FROM Activity " "WHERE Type = 'Shelf' " @@ -3101,7 +3101,7 @@ class KOBOTOUCH(KOBO): "WHERE Shelf.Name = Activity.Id " f"AND Shelf._IsDeleted = {false})" ) - debug_print("KoboTouch:delete_empty_bookshelves - delete_activity_query=", delete_activity_query) + debug_print('KoboTouch:delete_empty_bookshelves - delete_activity_query=', delete_activity_query) cursor = connection.cursor() cursor.execute(delete_query, ignore_collections_values) @@ -3110,7 +3110,7 @@ class KOBOTOUCH(KOBO): cursor.execute(delete_activity_query) cursor.close() - debug_print("KoboTouch:delete_empty_bookshelves - end") + debug_print('KoboTouch:delete_empty_bookshelves - end') def get_bookshelflist(self, connection): # Retrieve the list of booksehelves @@ -3193,11 +3193,11 @@ class KOBOTOUCH(KOBO): bookshelf_name, time.strftime(self.TIMESTAMP_STRING, time.gmtime()), bookshelf_name, - "false", - "true", - "false", + 'false', + 'true', + 'false', ) - shelf_type = "UserTag" # if self.supports_reading_list else None + shelf_type = 'UserTag' # if self.supports_reading_list else None if self.dbversion < 64: addquery += ' ("CreationDate","InternalName","LastModified","Name","_IsDeleted","_IsVisible","_IsSynced")'\ ' VALUES (?, ?, ?, ?, ?, ?, ?)' @@ -3246,9 +3246,9 @@ class KOBOTOUCH(KOBO): values.append(ContentID) else: if oncard == 'carda': - query += ' WHERE ContentID like \'file:///mnt/sd/%\'' + query += " WHERE ContentID like 'file:///mnt/sd/%'" elif oncard != 'carda' and oncard != 'cardb': - query += ' WHERE ContentID not like \'file:///mnt/sd/%\'' + query += " WHERE ContentID not like 'file:///mnt/sd/%'" if bookshelves: placeholder = '?' @@ -3261,7 +3261,7 @@ class KOBOTOUCH(KOBO): cursor.execute(query, values) cursor.close() - debug_print("KoboTouch:remove_from_bookshelf - end") + debug_print('KoboTouch:remove_from_bookshelf - end') # No longer used, but keep for a little bit. def set_series(self, connection, book): @@ -3289,7 +3289,7 @@ class KOBOTOUCH(KOBO): elif book.series_index is None: # This should never happen, but... update_values = (book.series, None, book.contentID, ) else: - update_values = (book.series, "%g"%book.series_index, book.contentID, ) + update_values = (book.series, '%g'%book.series_index, book.contentID, ) cursor = connection.cursor() try: @@ -3304,7 +3304,7 @@ class KOBOTOUCH(KOBO): cursor.close() if show_debug: - debug_print("KoboTouch:set_series - end") + debug_print('KoboTouch:set_series - end') def set_core_metadata(self, connection, book, series_only=False): # debug_print('KoboTouch:set_core_metadata book="%s"' % book.title) @@ -3319,9 +3319,9 @@ class KOBOTOUCH(KOBO): new_value = None else: new_value = new_value if len(new_value.strip()) else None - if new_value is not None and new_value.startswith("PLUGBOARD TEMPLATE ERROR"): + if new_value is not None and new_value.startswith('PLUGBOARD TEMPLATE ERROR'): debug_print("KoboTouch:generate_update_from_template template error - template='%s'" % template) - debug_print("KoboTouch:generate_update_from_template - new_value=", new_value) + debug_print('KoboTouch:generate_update_from_template - new_value=', new_value) # debug_print( # f"KoboTouch:generate_update_from_template - {book.title} - column_name='{column_name}'," @@ -3366,7 +3366,7 @@ class KOBOTOUCH(KOBO): if newmi.series is not None: new_series = newmi.series try: - new_series_number = "%g" % newmi.series_index + new_series_number = '%g' % newmi.series_index except: new_series_number = None else: @@ -3462,9 +3462,9 @@ class KOBOTOUCH(KOBO): new_subtitle = None else: new_subtitle = book.subtitle if len(book.subtitle.strip()) else None - if new_subtitle is not None and new_subtitle.startswith("PLUGBOARD TEMPLATE ERROR"): + if new_subtitle is not None and new_subtitle.startswith('PLUGBOARD TEMPLATE ERROR'): debug_print("KoboTouch:set_core_metadata subtitle template error - self.subtitle_template='%s'" % self.subtitle_template) - debug_print("KoboTouch:set_core_metadata - new_subtitle=", new_subtitle) + debug_print('KoboTouch:set_core_metadata - new_subtitle=', new_subtitle) if (new_subtitle is not None and (book.kobo_subtitle is None or book.subtitle != book.kobo_subtitle)) or \ (new_subtitle is None and book.kobo_subtitle is not None): @@ -3531,7 +3531,7 @@ class KOBOTOUCH(KOBO): cursor.close() if show_debug: - debug_print("KoboTouch:set_core_metadata - end") + debug_print('KoboTouch:set_core_metadata - end') @classmethod def config_widget(cls): @@ -3556,7 +3556,7 @@ class KOBOTOUCH(KOBO): try: return getattr(cls.opts, key) except: - debug_print("KoboTouch::get_prefs - probably an extra_customization:", key) + debug_print('KoboTouch::get_prefs - probably an extra_customization:', key) return None @classmethod @@ -4068,7 +4068,7 @@ class KOBOTOUCH(KOBO): debug_print('The database has been upgraded past supported version') self.report_progress(1.0, _('Removing books from device...')) from calibre.devices.errors import UserFeedback - raise UserFeedback(_("Kobo database version unsupported - See details"), + raise UserFeedback(_('Kobo database version unsupported - See details'), _('Your Kobo is running an updated firmware/database version.' ' As calibre does not know about this updated firmware,' ' database editing is disabled, to prevent corruption.' @@ -4105,14 +4105,14 @@ class KOBOTOUCH(KOBO): def is_supported_fwversion(self): # Starting with firmware version 3.19.x, the last number appears to be is a # build number. It can be safely ignored when testing the firmware version. - debug_print("KoboTouch::is_supported_fwversion - self.fwversion[:2]", self.fwversion[:2]) + debug_print('KoboTouch::is_supported_fwversion - self.fwversion[:2]', self.fwversion[:2]) return self.fwversion[:2] > self.max_supported_fwversion @classmethod def migrate_old_settings(cls, settings): - debug_print("KoboTouch::migrate_old_settings - start") - debug_print("KoboTouch::migrate_old_settings - settings.extra_customization=", settings.extra_customization) - debug_print("KoboTouch::migrate_old_settings - For class=", cls.name) + debug_print('KoboTouch::migrate_old_settings - start') + debug_print('KoboTouch::migrate_old_settings - settings.extra_customization=', settings.extra_customization) + debug_print('KoboTouch::migrate_old_settings - For class=', cls.name) count_options = 0 OPT_COLLECTIONS = count_options @@ -4146,11 +4146,11 @@ class KOBOTOUCH(KOBO): # the total number of options. if cls == KOBOTOUCH or len(settings.extra_customization) >= count_options: config = cls._config() - debug_print("KoboTouch::migrate_old_settings - config.preferences=", config.preferences) - debug_print("KoboTouch::migrate_old_settings - settings need to be migrated") + debug_print('KoboTouch::migrate_old_settings - config.preferences=', config.preferences) + debug_print('KoboTouch::migrate_old_settings - settings need to be migrated') settings.manage_collections = True settings.collections_columns = settings.extra_customization[OPT_COLLECTIONS] - debug_print("KoboTouch::migrate_old_settings - settings.collections_columns=", settings.collections_columns) + debug_print('KoboTouch::migrate_old_settings - settings.collections_columns=', settings.collections_columns) settings.create_collections = settings.extra_customization[OPT_CREATE_BOOKSHELVES] settings.delete_empty_collections = settings.extra_customization[OPT_DELETE_BOOKSHELVES] @@ -4183,7 +4183,7 @@ class KOBOTOUCH(KOBO): debugging_title = settings.extra_customization[OPT_SUPPORT_NEWER_FIRMWARE] start_subclass_extra_options = OPT_SUPPORT_NEWER_FIRMWARE + 1 else: - debug_print("KoboTouch::migrate_old_settings - Have all options") + debug_print('KoboTouch::migrate_old_settings - Have all options') settings.update_series = settings.extra_customization[OPT_UPDATE_SERIES_DETAILS] settings.modify_css = settings.extra_customization[OPT_MODIFY_CSS] settings.support_newer_firmware = settings.extra_customization[OPT_SUPPORT_NEWER_FIRMWARE] @@ -4230,9 +4230,9 @@ class KOBOTOUCH(KOBO): prints(placeholders%row) i += 1 if i == 0: - prints("No shelves found!!") + prints('No shelves found!!') else: - prints("Number of shelves=%d"%i) + prints('Number of shelves=%d'%i) prints('\nBooks on shelves on device:') cursor.execute(shelfcontent_query) @@ -4242,16 +4242,16 @@ class KOBOTOUCH(KOBO): prints(placeholders%row) i += 1 if i == 0: - prints("No books are on any shelves!!") + prints('No books are on any shelves!!') else: - prints("Number of shelved books=%d"%i) + prints('Number of shelved books=%d'%i) cursor.close() debug_print('KoboTouch:dump_bookshelves - end') def __str__(self, *args, **kwargs): options = ', '.join([f'{x.name}: {self.get_pref(x.name)}' for x in self._config().preferences]) - return f"Driver:{self.name}, Options - {options}" + return f'Driver:{self.name}, Options - {options}' if __name__ == '__main__': @@ -4265,8 +4265,8 @@ if __name__ == '__main__': devs = scanner.devices # debug_print("unit test: devs.__class__=", devs.__class__) # debug_print("unit test: devs.__class__=", devs.__class__.__name__) - debug_print("unit test: devs=", devs) - debug_print("unit test: dev=", dev) + debug_print('unit test: devs=', devs) + debug_print('unit test: dev=', dev) # cd = dev.detect_managed_devices(devs) # if cd is None: # raise ValueError('Failed to detect KOBOTOUCH device') diff --git a/src/calibre/devices/kobo/kobotouch_config.py b/src/calibre/devices/kobo/kobotouch_config.py index eb3a7459b9..179e2b18bd 100644 --- a/src/calibre/devices/kobo/kobotouch_config.py +++ b/src/calibre/devices/kobo/kobotouch_config.py @@ -52,7 +52,7 @@ class KOBOTOUCHConfig(TabbedDeviceConfig): self.tab1 = Tab1Config(self, self.device) self.tab2 = Tab2Config(self, self.device) - self.addDeviceTab(self.tab1, _("Collections, covers && uploads")) + self.addDeviceTab(self.tab1, _('Collections, covers && uploads')) self.addDeviceTab(self.tab2, _('Metadata, on device && advanced')) def get_pref(self, key): @@ -92,7 +92,7 @@ class KOBOTOUCHConfig(TabbedDeviceConfig): return self.tab2.metadata_options def commit(self): - debug_print("KOBOTOUCHConfig::commit: start") + debug_print('KOBOTOUCHConfig::commit: start') p = super().commit() p['manage_collections'] = self.manage_collections @@ -195,14 +195,14 @@ class BookUploadsGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super().__init__(parent, device) - self.setTitle(_("Uploading of books")) + self.setTitle(_('Uploading of books')) self.options_layout = QGridLayout() - self.options_layout.setObjectName("options_layout") + self.options_layout.setObjectName('options_layout') self.setLayout(self.options_layout) self.modify_css_checkbox = create_checkbox( - _("Modify CSS"), + _('Modify CSS'), _('This allows addition of user CSS rules and removal of some CSS. ' 'When sending a book, the driver adds the contents of {0} to all stylesheets in the EPUB. ' 'This file is searched for in the root folder of the main memory of the device. ' @@ -211,7 +211,7 @@ class BookUploadsGroupBox(DeviceOptionsGroupBox): device.get_pref('modify_css') ) self.override_kobo_replace_existing_checkbox = create_checkbox( - _("Do not treat replacements as new books"), + _('Do not treat replacements as new books'), _('When a new book is side-loaded, the Kobo firmware imports details of the book into the internal database. ' 'Even if the book is a replacement for an existing book, the Kobo will remove the book from the database and then treat it as a new book. ' 'This means that the reading status, bookmarks and collections for the book will be lost. ' @@ -237,10 +237,10 @@ class CollectionsGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super().__init__(parent, device) - self.setTitle(_("Collections")) + self.setTitle(_('Collections')) self.options_layout = QGridLayout() - self.options_layout.setObjectName("options_layout") + self.options_layout.setObjectName('options_layout') self.setLayout(self.options_layout) self.setCheckable(True) @@ -248,7 +248,7 @@ class CollectionsGroupBox(DeviceOptionsGroupBox): self.setToolTip(wrap_msg(_('Create new collections on the Kobo if they do not exist. This is only for firmware V2.0.0 or later.'))) self.use_collections_columns_checkbox = create_checkbox( - _("Collections columns:"), + _('Collections columns:'), _('Use a column to generate collections.'), device.get_pref('use_collections_columns') ) @@ -259,7 +259,7 @@ class CollectionsGroupBox(DeviceOptionsGroupBox): self.collections_columns_edit.setText(device.get_pref('collections_columns')) self.use_collections_template_checkbox = create_checkbox( - _("Collections template:"), + _('Collections template:'), _('Use a template to generate collections.'), device.get_pref('use_collections_template') ) @@ -272,7 +272,7 @@ class CollectionsGroupBox(DeviceOptionsGroupBox): ) self.create_collections_checkbox = create_checkbox( - _("Create collections"), + _('Create collections'), _('Create new collections on the Kobo if they do not exist. This is only for firmware V2.0.0 or later.'), device.get_pref('create_collections') ) @@ -346,10 +346,10 @@ class CoversGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super().__init__(parent, device) - self.setTitle(_("Upload covers")) + self.setTitle(_('Upload covers')) self.options_layout = QGridLayout() - self.options_layout.setObjectName("options_layout") + self.options_layout.setObjectName('options_layout') self.setLayout(self.options_layout) self.setCheckable(True) @@ -465,14 +465,14 @@ class DeviceListGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super().__init__(parent, device) - self.setTitle(_("Show as on device")) + self.setTitle(_('Show as on device')) self.options_layout = QGridLayout() - self.options_layout.setObjectName("options_layout") + self.options_layout.setObjectName('options_layout') self.setLayout(self.options_layout) self.show_recommendations_checkbox = create_checkbox( - _("Show recommendations"), + _('Show recommendations'), _('Kobo shows recommendations on the device. In some cases these have ' 'files but in other cases they are just pointers to the web site to buy. ' 'Enable if you wish to see/delete them.'), @@ -480,7 +480,7 @@ class DeviceListGroupBox(DeviceOptionsGroupBox): ) self.show_archived_books_checkbox = create_checkbox( - _("Show archived books"), + _('Show archived books'), _('Archived books are listed on the device but need to be downloaded to read.' ' Use this option to show these books and match them with books in the calibre library.'), device.get_pref('show_archived_books') @@ -514,15 +514,15 @@ class DeviceListGroupBox(DeviceOptionsGroupBox): class AdvancedGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): - super().__init__(parent, device, _("Advanced options")) + super().__init__(parent, device, _('Advanced options')) # self.setTitle(_("Advanced Options")) self.options_layout = QGridLayout() - self.options_layout.setObjectName("options_layout") + self.options_layout.setObjectName('options_layout') self.setLayout(self.options_layout) self.support_newer_firmware_checkbox = create_checkbox( - _("Attempt to support newer firmware"), + _('Attempt to support newer firmware'), _('Kobo routinely updates the firmware and the ' 'database version. With this option calibre will attempt ' 'to perform full read-write functionality - Here be Dragons!! ' @@ -533,7 +533,7 @@ class AdvancedGroupBox(DeviceOptionsGroupBox): ) self.debugging_title_checkbox = create_checkbox( - _("Title to test when debugging"), + _('Title to test when debugging'), _('Part of title of a book that can be used when doing some tests for debugging. ' 'The test is to see if the string is contained in the title of a book. ' 'The better the match, the less extraneous output.'), @@ -564,10 +564,10 @@ class MetadataGroupBox(DeviceOptionsGroupBox): def __init__(self, parent, device): super().__init__(parent, device) - self.setTitle(_("Update metadata on the device")) + self.setTitle(_('Update metadata on the device')) self.options_layout = QGridLayout() - self.options_layout.setObjectName("options_layout") + self.options_layout.setObjectName('options_layout') self.setLayout(self.options_layout) self.setCheckable(True) @@ -576,7 +576,7 @@ class MetadataGroupBox(DeviceOptionsGroupBox): 'Be careful when doing this as it will take time and could make the initial connection take a long time.'))) self.update_series_checkbox = create_checkbox( - _("Set series information"), + _('Set series information'), _('The book lists on the Kobo devices can display series information. ' 'This is not read by the device from the sideloaded books. ' 'Series information can only be added to the device after the ' @@ -585,7 +585,7 @@ class MetadataGroupBox(DeviceOptionsGroupBox): device.get_pref('update_series') ) self.force_series_id_checkbox = create_checkbox( - _("Force series ID"), + _('Force series ID'), _('Kobo devices use a SeriesID to distinguish between different series. ' 'Purchased books have a SeriesID assigned by Kobo. Sideloaded books ' 'have a SeriesID assigned by calibre, which is usually different. ' @@ -595,7 +595,7 @@ class MetadataGroupBox(DeviceOptionsGroupBox): device.get_pref('force_series_id') ) self.update_core_metadata_checkbox = create_checkbox( - _("Update metadata on Book Details pages"), + _('Update metadata on Book Details pages'), _('This will update the metadata in the device database when the device is connected. ' 'The metadata updated is displayed on the device in the library and the Book details page. ' 'This is the title, authors, comments/synopsis, series name and number, publisher and published Date, ISBN and language. ' @@ -605,57 +605,57 @@ class MetadataGroupBox(DeviceOptionsGroupBox): ) self.update_purchased_kepubs_checkbox = create_checkbox( - _("Update purchased books"), + _('Update purchased books'), _('Update books purchased from Kobo and downloaded to the device.' ), device.get_pref('update_purchased_kepubs') ) self.update_subtitle_checkbox = create_checkbox( - _("Subtitle"), + _('Subtitle'), _('Update the subtitle on the device using a template.'), device.get_pref('update_subtitle') ) self.subtitle_template_edit = TemplateConfig( device.get_pref('subtitle_template'), - tooltip=_("Enter a template to use to set the subtitle. " - "If the template is empty, the subtitle will be cleared." + tooltip=_('Enter a template to use to set the subtitle. ' + 'If the template is empty, the subtitle will be cleared.' ) ) self.update_bookstats_checkbox = create_checkbox( - _("Book stats"), + _('Book stats'), _('Update the book stats '), device.get_pref('update_bookstats') ) self.bookstats_wordcount_template_edit = TemplateConfig( device.get_pref('bookstats_wordcount_template'), - label=_("Words:"), - tooltip=_("Enter a template to use to set the word count for the book. " - "If the template is empty, the word count will be cleared." + label=_('Words:'), + tooltip=_('Enter a template to use to set the word count for the book. ' + 'If the template is empty, the word count will be cleared.' ) ) self.bookstats_pagecount_template_edit = TemplateConfig( device.get_pref('bookstats_pagecount_template'), - label=_("Pages:"), - tooltip=_("Enter a template to use to set the page count for the book. " - "If the template is empty, the page count will be cleared." + label=_('Pages:'), + tooltip=_('Enter a template to use to set the page count for the book. ' + 'If the template is empty, the page count will be cleared.' ) ) self.bookstats_timetoread_label = QLabel(_('Hours to read estimates:')) self.bookstats_timetoread_upper_template_edit = TemplateConfig( device.get_pref('bookstats_timetoread_upper_template'), - label=_("Upper:"), - tooltip=_("Enter a template to use to set the upper estimate of the time to read for the book. " - "The estimate is in hours. " - "If the template is empty, the time will be cleared." + label=_('Upper:'), + tooltip=_('Enter a template to use to set the upper estimate of the time to read for the book. ' + 'The estimate is in hours. ' + 'If the template is empty, the time will be cleared.' ) ) self.bookstats_timetoread_lower_template_edit = TemplateConfig( device.get_pref('bookstats_timetoread_lower_template'), - label=_("Lower:"), - tooltip=_("Enter a template to use to set the lower estimate of the time to read for the book. " - "The estimate is in hours. " - "If the template is empty, the time will be cleared." + label=_('Lower:'), + tooltip=_('Enter a template to use to set the lower estimate of the time to read for the book. ' + 'The estimate is in hours. ' + 'If the template is empty, the time will be cleared.' ) ) @@ -842,7 +842,7 @@ if __name__ == '__main__': s = DeviceScanner() s.scan() app = Application([]) - debug_print("KOBOTOUCH:", KOBOTOUCH) + debug_print('KOBOTOUCH:', KOBOTOUCH) dev = KOBOTOUCH(None) # dev.startup() # cd = dev.detect_managed_devices(s.devices) diff --git a/src/calibre/devices/mtp/unix/driver.py b/src/calibre/devices/mtp/unix/driver.py index d3d8047e51..33ef45461c 100644 --- a/src/calibre/devices/mtp/unix/driver.py +++ b/src/calibre/devices/mtp/unix/driver.py @@ -234,7 +234,7 @@ class MTP_DEVICE(MTPDeviceBase): try: storage = sorted_storage(self.dev.storage_info) except self.libmtp.MTPError as e: - if "The device has no storage information." in str(e): + if 'The device has no storage information.' in str(e): # This happens on newer Android devices while waiting for # the user to allow access. Apparently what happens is # that when the user clicks allow, the device disconnects diff --git a/src/calibre/devices/mtp/windows/driver.py b/src/calibre/devices/mtp/windows/driver.py index 6f8efb3425..5203a30495 100644 --- a/src/calibre/devices/mtp/windows/driver.py +++ b/src/calibre/devices/mtp/windows/driver.py @@ -169,7 +169,7 @@ class MTP_DEVICE(MTPDeviceBase): try: pnp_ids = frozenset(self.wpd.enumerate_devices()) except: - p("Failed to get list of PNP ids on system") + p('Failed to get list of PNP ids on system') p(traceback.format_exc()) return False diff --git a/src/calibre/devices/paladin/driver.py b/src/calibre/devices/paladin/driver.py index 54d60f00ac..750aef630e 100644 --- a/src/calibre/devices/paladin/driver.py +++ b/src/calibre/devices/paladin/driver.py @@ -82,7 +82,7 @@ class PALADIN(USBMS): bl = USBMS.books(self, oncard=oncard, end_session=end_session) dbpath = self.normalize_path(prefix + DBPATH) - debug_print("SQLite DB Path: " + dbpath) + debug_print('SQLite DB Path: ' + dbpath) with closing(apsw.Connection(dbpath)) as connection: cursor = connection.cursor() @@ -122,10 +122,10 @@ class PALADIN(USBMS): try: device_offset = max(time_offsets, key=lambda a: time_offsets.get(a)) - debug_print("Device Offset: %d ms"%device_offset) + debug_print('Device Offset: %d ms'%device_offset) self.device_offset = device_offset except ValueError: - debug_print("No Books To Detect Device Offset.") + debug_print('No Books To Detect Device Offset.') for idx, book in enumerate(bl): query = 'SELECT _id, thumbnail FROM books WHERE filename = ?' @@ -174,7 +174,7 @@ class PALADIN(USBMS): if self.plugboard_func: plugboard = self.plugboard_func(self.__class__.__name__, 'device_db', self.plugboards) - debug_print("PALADIN: Using Plugboard", plugboard) + debug_print('PALADIN: Using Plugboard', plugboard) prefix = self._card_a_prefix if oncard == 'carda' else self._main_prefix if prefix is None: @@ -183,7 +183,7 @@ class PALADIN(USBMS): source_id = 1 if oncard == 'carda' else 0 dbpath = self.normalize_path(prefix + DBPATH) - debug_print("SQLite DB Path: " + dbpath) + debug_print('SQLite DB Path: ' + dbpath) collections = booklist.get_collections(collections_attributes) @@ -199,7 +199,7 @@ class PALADIN(USBMS): try: cursor = connection.cursor() - debug_print("Removing Orphaned Collection Records") + debug_print('Removing Orphaned Collection Records') # Purge any collections references that point into the abyss query = 'DELETE FROM booktags WHERE book_id NOT IN (SELECT _id FROM books)' @@ -207,7 +207,7 @@ class PALADIN(USBMS): query = 'DELETE FROM booktags WHERE tag_id NOT IN (SELECT _id FROM tags)' cursor.execute(query) - debug_print("Removing Orphaned Book Records") + debug_print('Removing Orphaned Book Records') cursor.close() except Exception: @@ -249,7 +249,7 @@ class PALADIN(USBMS): sequence_max = sequence_min sequence_dirty = 0 - debug_print("Book Sequence Min: %d, Source Id: %d"%(sequence_min,source_id)) + debug_print('Book Sequence Min: %d, Source Id: %d'%(sequence_min,source_id)) try: cursor = connection.cursor() @@ -283,7 +283,7 @@ class PALADIN(USBMS): # If the database is 'dirty', then we should fix up the Ids and the sequence number if sequence_dirty == 1: - debug_print("Book Sequence Dirty for Source Id: %d"%source_id) + debug_print('Book Sequence Dirty for Source Id: %d'%source_id) sequence_max = sequence_max + 1 for book, bookId in db_books.items(): if bookId < sequence_min: @@ -302,7 +302,7 @@ class PALADIN(USBMS): cursor.execute(query, t) self.set_database_sequence_id(connection, 'books', sequence_max) - debug_print("Book Sequence Max: %d, Source Id: %d"%(sequence_max,source_id)) + debug_print('Book Sequence Max: %d, Source Id: %d'%(sequence_max,source_id)) cursor.close() return db_books @@ -386,7 +386,7 @@ class PALADIN(USBMS): sequence_max = sequence_min sequence_dirty = 0 - debug_print("Collection Sequence Min: %d, Source Id: %d"%(sequence_min,source_id)) + debug_print('Collection Sequence Min: %d, Source Id: %d'%(sequence_min,source_id)) try: cursor = connection.cursor() @@ -415,7 +415,7 @@ class PALADIN(USBMS): # If the database is 'dirty', then we should fix up the Ids and the sequence number if sequence_dirty == 1: - debug_print("Collection Sequence Dirty for Source Id: %d"%source_id) + debug_print('Collection Sequence Dirty for Source Id: %d'%source_id) sequence_max = sequence_max + 1 for collection, collectionId in db_collections.items(): if collectionId < sequence_min: @@ -434,13 +434,13 @@ class PALADIN(USBMS): cursor.execute(query, t) self.set_database_sequence_id(connection, 'tags', sequence_max) - debug_print("Collection Sequence Max: %d, Source Id: %d"%(sequence_max,source_id)) + debug_print('Collection Sequence Max: %d, Source Id: %d'%(sequence_max,source_id)) # Fix up the collections table now... sequence_dirty = 0 sequence_max = sequence_min - debug_print("Collections Sequence Min: %d, Source Id: %d"%(sequence_min,source_id)) + debug_print('Collections Sequence Min: %d, Source Id: %d'%(sequence_min,source_id)) query = 'SELECT _id FROM booktags' cursor.execute(query) @@ -454,7 +454,7 @@ class PALADIN(USBMS): sequence_max = max(sequence_max, row[0]) if sequence_dirty == 1: - debug_print("Collections Sequence Dirty for Source Id: %d"%source_id) + debug_print('Collections Sequence Dirty for Source Id: %d'%source_id) sequence_max = sequence_max + 1 for pairId in db_collection_pairs: if pairId < sequence_min: @@ -465,7 +465,7 @@ class PALADIN(USBMS): sequence_max = sequence_max + 1 self.set_database_sequence_id(connection, 'booktags', sequence_max) - debug_print("Collections Sequence Max: %d, Source Id: %d"%(sequence_max,source_id)) + debug_print('Collections Sequence Max: %d, Source Id: %d'%(sequence_max,source_id)) cursor.close() return db_collections diff --git a/src/calibre/devices/prs505/sony_cache.py b/src/calibre/devices/prs505/sony_cache.py index a676cfad08..864ff6d9e9 100644 --- a/src/calibre/devices/prs505/sony_cache.py +++ b/src/calibre/devices/prs505/sony_cache.py @@ -47,11 +47,11 @@ EMPTY_EXT_CACHE = b'''\ ''' MIME_MAP = { - "lrf" : "application/x-sony-bbeb", + 'lrf' : 'application/x-sony-bbeb', 'lrx' : 'application/x-sony-bbeb', - "rtf" : "application/rtf", - "pdf" : "application/pdf", - "txt" : "text/plain" , + 'rtf' : 'application/rtf', + 'pdf' : 'application/pdf', + 'txt' : 'text/plain' , 'epub': 'application/epub+zip', } @@ -71,9 +71,9 @@ def strptime(src): def strftime(epoch, zone=time.localtime): try: - src = time.strftime("%w, %d %m %Y %H:%M:%S GMT", zone(epoch)).split() + src = time.strftime('%w, %d %m %Y %H:%M:%S GMT', zone(epoch)).split() except: - src = time.strftime("%w, %d %m %Y %H:%M:%S GMT", zone()).split() + src = time.strftime('%w, %d %m %Y %H:%M:%S GMT', zone()).split() src[0] = INVERSE_DAY_MAP[int(src[0][:-1])]+',' src[2] = INVERSE_MONTH_MAP[int(src[2])] @@ -460,7 +460,7 @@ class XMLCache: if not self.is_sony_periodical(book): return record.set('conformsTo', - "http://xmlns.sony.net/e-book/prs/periodicals/1.0/newspaper/1.0") + 'http://xmlns.sony.net/e-book/prs/periodicals/1.0/newspaper/1.0') record.set('description', '') @@ -649,10 +649,10 @@ class XMLCache: debug_print("Use localtime TZ and tz='0' for new book", book.lpath) elif ltz_count >= gtz_count: tz = time.localtime - debug_print("Use localtime TZ for new book", book.lpath) + debug_print('Use localtime TZ for new book', book.lpath) else: tz = time.gmtime - debug_print("Use GMT TZ for new book", book.lpath) + debug_print('Use GMT TZ for new book', book.lpath) date = strftime(timestamp, zone=tz) record.set('date', clean(date)) try: diff --git a/src/calibre/devices/prst1/driver.py b/src/calibre/devices/prst1/driver.py index 47158e6bc2..2e678df3dc 100644 --- a/src/calibre/devices/prst1/driver.py +++ b/src/calibre/devices/prst1/driver.py @@ -167,7 +167,7 @@ class PRST1(USBMS): bl = USBMS.books(self, oncard=oncard, end_session=end_session) dbpath = self.normalize_path(prefix + DBPATH) - debug_print("SQLite DB Path: " + dbpath) + debug_print('SQLite DB Path: ' + dbpath) with closing(sqlite.connect(dbpath)) as connection: # Replace undecodable characters in the db instead of erroring out @@ -210,10 +210,10 @@ class PRST1(USBMS): try: device_offset = max(time_offsets, key=lambda a: time_offsets.get(a)) - debug_print("Device Offset: %d ms"%device_offset) + debug_print('Device Offset: %d ms'%device_offset) self.device_offset = device_offset except ValueError: - debug_print("No Books To Detect Device Offset.") + debug_print('No Books To Detect Device Offset.') for idx, book in enumerate(bl): query = 'SELECT _id, thumbnail FROM books WHERE file_path = ?' @@ -263,7 +263,7 @@ class PRST1(USBMS): if self.plugboard_func: plugboard = self.plugboard_func(self.__class__.__name__, 'device_db', self.plugboards) - debug_print("PRST1: Using Plugboard", plugboard) + debug_print('PRST1: Using Plugboard', plugboard) prefix = self._card_a_prefix if oncard == 'carda' else self._main_prefix if prefix is None: @@ -272,7 +272,7 @@ class PRST1(USBMS): source_id = 1 if oncard == 'carda' else 0 dbpath = self.normalize_path(prefix + DBPATH) - debug_print("SQLite DB Path: " + dbpath) + debug_print('SQLite DB Path: ' + dbpath) collections = booklist.get_collections(collections_attributes) @@ -290,7 +290,7 @@ class PRST1(USBMS): try: cursor = connection.cursor() - debug_print("Removing Orphaned Collection Records") + debug_print('Removing Orphaned Collection Records') # Purge any collections references that point into the abyss query = 'DELETE FROM collections WHERE content_id NOT IN (SELECT _id FROM books)' @@ -298,7 +298,7 @@ class PRST1(USBMS): query = 'DELETE FROM collections WHERE collection_id NOT IN (SELECT _id FROM collection)' cursor.execute(query) - debug_print("Removing Orphaned Book Records") + debug_print('Removing Orphaned Book Records') # Purge any references to books not in this database # Idea is to prevent any spill-over where these wind up applying to some other book @@ -362,7 +362,7 @@ class PRST1(USBMS): sequence_max = sequence_min sequence_dirty = 0 - debug_print("Book Sequence Min: %d, Source Id: %d"%(sequence_min,source_id)) + debug_print('Book Sequence Min: %d, Source Id: %d'%(sequence_min,source_id)) try: cursor = connection.cursor() @@ -396,7 +396,7 @@ class PRST1(USBMS): # If the database is 'dirty', then we should fix up the Ids and the sequence number if sequence_dirty == 1: - debug_print("Book Sequence Dirty for Source Id: %d"%source_id) + debug_print('Book Sequence Dirty for Source Id: %d'%source_id) sequence_max = sequence_max + 1 for book, bookId in db_books.items(): if bookId < sequence_min: @@ -433,7 +433,7 @@ class PRST1(USBMS): cursor.execute(query, t) self.set_database_sequence_id(connection, 'books', sequence_max) - debug_print("Book Sequence Max: %d, Source Id: %d"%(sequence_max,source_id)) + debug_print('Book Sequence Max: %d, Source Id: %d'%(sequence_max,source_id)) cursor.close() return db_books @@ -534,7 +534,7 @@ class PRST1(USBMS): sequence_max = sequence_min sequence_dirty = 0 - debug_print("Collection Sequence Min: %d, Source Id: %d"%(sequence_min,source_id)) + debug_print('Collection Sequence Min: %d, Source Id: %d'%(sequence_min,source_id)) try: cursor = connection.cursor() @@ -563,7 +563,7 @@ class PRST1(USBMS): # If the database is 'dirty', then we should fix up the Ids and the sequence number if sequence_dirty == 1: - debug_print("Collection Sequence Dirty for Source Id: %d"%source_id) + debug_print('Collection Sequence Dirty for Source Id: %d'%source_id) sequence_max = sequence_max + 1 for collection, collectionId in db_collections.items(): if collectionId < sequence_min: @@ -582,13 +582,13 @@ class PRST1(USBMS): cursor.execute(query, t) self.set_database_sequence_id(connection, 'collection', sequence_max) - debug_print("Collection Sequence Max: %d, Source Id: %d"%(sequence_max,source_id)) + debug_print('Collection Sequence Max: %d, Source Id: %d'%(sequence_max,source_id)) # Fix up the collections table now... sequence_dirty = 0 sequence_max = sequence_min - debug_print("Collections Sequence Min: %d, Source Id: %d"%(sequence_min,source_id)) + debug_print('Collections Sequence Min: %d, Source Id: %d'%(sequence_min,source_id)) query = 'SELECT _id FROM collections' cursor.execute(query) @@ -602,7 +602,7 @@ class PRST1(USBMS): sequence_max = max(sequence_max, row[0]) if sequence_dirty == 1: - debug_print("Collections Sequence Dirty for Source Id: %d"%source_id) + debug_print('Collections Sequence Dirty for Source Id: %d'%source_id) sequence_max = sequence_max + 1 for pairId in db_collection_pairs: if pairId < sequence_min: @@ -613,7 +613,7 @@ class PRST1(USBMS): sequence_max = sequence_max + 1 self.set_database_sequence_id(connection, 'collections', sequence_max) - debug_print("Collections Sequence Max: %d, Source Id: %d"%(sequence_max,source_id)) + debug_print('Collections Sequence Max: %d, Source Id: %d'%(sequence_max,source_id)) cursor.close() return db_collections @@ -727,7 +727,7 @@ class PRST1(USBMS): metadata.lpath = filepath.partition(prefix)[2] metadata.lpath = metadata.lpath.replace('\\', '/') dbpath = self.normalize_path(prefix + DBPATH) - debug_print("SQLite DB Path: " + dbpath) + debug_print('SQLite DB Path: ' + dbpath) with closing(sqlite.connect(dbpath)) as connection: cursor = connection.cursor() diff --git a/src/calibre/devices/smart_device_app/driver.py b/src/calibre/devices/smart_device_app/driver.py index 2da096f7bc..136d7020e2 100644 --- a/src/calibre/devices/smart_device_app/driver.py +++ b/src/calibre/devices/smart_device_app/driver.py @@ -48,7 +48,7 @@ from polyglot.builtins import as_bytes, iteritems, itervalues def synchronous(tlockname): - """A decorator to place an instance based lock around a method """ + '''A decorator to place an instance based lock around a method ''' def _synched(func): @wraps(func) @@ -96,7 +96,7 @@ class ConnectionListener(Thread): if not self.all_ip_addresses: self.all_ip_addresses = get_all_ips() if self.all_ip_addresses: - self.driver._debug("All IP addresses", self.all_ip_addresses) + self.driver._debug('All IP addresses', self.all_ip_addresses) if not self.driver.connection_queue.empty(): d = currently_connected_device.device @@ -485,7 +485,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): except: today = time.localtime() date = (today[0], today[1], today[2]) - template = "{title}_%d-%d-%d" % date + template = '{title}_%d-%d-%d' % date use_subdirs = self.SUPPORTS_SUB_DIRS and settings.use_subdirs from calibre.library.save_to_disk import config, get_components @@ -854,7 +854,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): json_metadata[key]['book'] = self.json_codec.encode_book_metadata(book['book']) json_metadata[key]['last_used'] = book['last_used'] result = as_bytes(json.dumps(json_metadata, indent=2, default=to_json)) - fd.write(("%0.7d\n"%(len(result)+1)).encode('ascii')) + fd.write(('%0.7d\n'%(len(result)+1)).encode('ascii')) fd.write(result) fd.write(b'\n') count += 1 @@ -1014,14 +1014,14 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): from functools import partial p = partial(prints, file=output) if self.is_connected: - p("A wireless device is connected") + p('A wireless device is connected') return True all_ip_addresses = get_all_ips() if all_ip_addresses: - p("All IP addresses", all_ip_addresses) + p('All IP addresses', all_ip_addresses) else: - p("No IP addresses found") - p("No device is connected") + p('No IP addresses found') + p('No device is connected') return False @synchronous('sync_lock') @@ -1123,7 +1123,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): self.client_device_name = result.get('deviceName', self.client_device_kind) self._debug('Client device name', self.client_device_name) - self.client_app_name = result.get('appName', "") + self.client_app_name = result.get('appName', '') self._debug('Client app name', self.client_app_name) self.app_version_number = result.get('ccVersionNumber', '0') self._debug('App version #:', self.app_version_number) @@ -1132,7 +1132,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): if (self.client_app_name == 'CalibreCompanion' and self.app_version_number < self.CURRENT_CC_VERSION): self._debug('Telling client to update') - self._call_client("DISPLAY_MESSAGE", + self._call_client('DISPLAY_MESSAGE', {'messageKind': self.MESSAGE_UPDATE_NEEDED, 'lastestKnownAppVersion': self.CURRENT_CC_VERSION}) except: @@ -1185,7 +1185,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): # bad password self._debug('password mismatch') try: - self._call_client("DISPLAY_MESSAGE", + self._call_client('DISPLAY_MESSAGE', {'messageKind': self.MESSAGE_PASSWORD_ERROR, 'currentLibraryName': self.current_library_name, 'currentLibraryUUID': library_uuid}) @@ -1242,7 +1242,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): @synchronous('sync_lock') def set_driveinfo_name(self, location_code, name): - self._update_driveinfo_record(self.driveinfo, "main", name) + self._update_driveinfo_record(self.driveinfo, 'main', name) self._call_client('SET_CALIBRE_DEVICE_NAME', {'location_code': 'main', 'name':name}) @@ -1625,7 +1625,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): device_prefs.set_overrides(manage_device_metadata='on_connect') def _show_message(self, message): - self._call_client("DISPLAY_MESSAGE", + self._call_client('DISPLAY_MESSAGE', {'messageKind': self.MESSAGE_SHOW_TOAST, 'message': message}) @@ -1685,8 +1685,8 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): self.have_bad_sync_columns = True elif fm[self.is_read_sync_col]['datatype'] != 'bool': self._debug('is_read_sync_col not bool type') - self._show_message(_("The read sync column %s is " - "not a Yes/No column")%self.is_read_sync_col) + self._show_message(_('The read sync column %s is ' + 'not a Yes/No column')%self.is_read_sync_col) self.have_bad_sync_columns = True if self.is_read_date_sync_col: @@ -1697,8 +1697,8 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): self.have_bad_sync_columns = True elif fm[self.is_read_date_sync_col]['datatype'] != 'datetime': self._debug('is_read_date_sync_col not date type') - self._show_message(_("The read date sync column %s is " - "not a date column")%self.is_read_date_sync_col) + self._show_message(_('The read date sync column %s is ' + 'not a date column')%self.is_read_date_sync_col) self.have_bad_sync_columns = True self.have_checked_sync_columns = True diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py index 1f51d8fb86..421098b535 100644 --- a/src/calibre/devices/usbms/device.py +++ b/src/calibre/devices/usbms/device.py @@ -673,7 +673,7 @@ class Device(DeviceConfig, DevicePlugin): hal = get_hal() vols = hal.get_volumes(d) if verbose: - print("FBSD:\t", vols) + print('FBSD:\t', vols) ok, mv = hal.mount_volumes(vols) if not ok: diff --git a/src/calibre/devices/usbms/driver.py b/src/calibre/devices/usbms/driver.py index a0199b2c52..20ff03b326 100644 --- a/src/calibre/devices/usbms/driver.py +++ b/src/calibre/devices/usbms/driver.py @@ -235,7 +235,7 @@ class USBMS(CLI, Device): def update_booklist(filename, path, prefix): changed = False # Ignore AppleDouble files - if filename.startswith("._"): + if filename.startswith('._'): return False if path_to_ext(filename) in all_formats and self.is_allowed_book_file(filename, path, prefix): try: diff --git a/src/calibre/devices/usbms/hal.py b/src/calibre/devices/usbms/hal.py index 89880eadc7..f6ca5e87f6 100644 --- a/src/calibre/devices/usbms/hal.py +++ b/src/calibre/devices/usbms/hal.py @@ -87,7 +87,7 @@ class HAL: time.sleep(1) loops += 1 if loops > 100: - raise Exception("ERROR: Timeout waiting for mount to complete") + raise Exception('ERROR: Timeout waiting for mount to complete') return self.prop(vol['dev'], 'volume.mount_point') def mount_volumes(self, volumes): @@ -106,19 +106,19 @@ class HAL: # Mount Point becomes Mount Path mp += '/' if DEBUG: - print("FBSD:\tmounted", vol['label'], "on", mp) + print('FBSD:\tmounted', vol['label'], 'on', mp) if mtd == 0: ans['_main_prefix'], ans['_main_vol'] = mp, vol['vol'] if DEBUG: - print("FBSD:\tmain = ", mp) + print('FBSD:\tmain = ', mp) elif mtd == 1: ans['_card_a_prefix'], ans['_card_a_vol'] = mp, vol['vol'] if DEBUG: - print("FBSD:\tcard a = ", mp) + print('FBSD:\tcard a = ', mp) elif mtd == 2: ans['_card_b_prefix'], ans['_card_b_vol'] = mp, vol['vol'] if DEBUG: - print("FBSD:\tcard b = ", mp) + print('FBSD:\tcard b = ', mp) break mtd += 1 diff --git a/src/calibre/devices/user_defined/driver.py b/src/calibre/devices/user_defined/driver.py index 8b4a75dd4f..c1f421b561 100644 --- a/src/calibre/devices/user_defined/driver.py +++ b/src/calibre/devices/user_defined/driver.py @@ -58,7 +58,7 @@ class USER_DEFINED(USBMS): 'Enter the folder where the books are to be stored. This folder ' 'is prepended to any send_to_device template') + '

', _('Swap main and card A') + ':::

' + _( - 'Check this box if the device\'s main memory is being seen as ' + "Check this box if the device's main memory is being seen as " 'card a and the card is being seen as main memory') + '

', ] EXTRA_CUSTOMIZATION_DEFAULT = [ diff --git a/src/calibre/devices/utils.py b/src/calibre/devices/utils.py index 071c3be9b6..763d967115 100644 --- a/src/calibre/devices/utils.py +++ b/src/calibre/devices/utils.py @@ -34,11 +34,11 @@ def sanity_check(on_card, files, card_prefixes, free_space): size += os.path.getsize(getattr(f, 'name', f)) if not on_card and size > free_space[0] - 2*1024*1024: - raise FreeSpaceError(_("There is insufficient free space in main memory")) + raise FreeSpaceError(_('There is insufficient free space in main memory')) if on_card == 'carda' and size > free_space[1] - 1024*1024: - raise FreeSpaceError(_("There is insufficient free space on the storage card")) + raise FreeSpaceError(_('There is insufficient free space on the storage card')) if on_card == 'cardb' and size > free_space[2] - 1024*1024: - raise FreeSpaceError(_("There is insufficient free space on the storage card")) + raise FreeSpaceError(_('There is insufficient free space on the storage card')) def build_template_regexp(template): @@ -91,7 +91,7 @@ def create_upload_path(mdata, fname, template, sanitize, except: today = time.localtime() date = (today[0], today[1], today[2]) - template = "{title}_%d-%d-%d" % date + template = '{title}_%d-%d-%d' % date fname = sanitize(fname) ext = path_type.splitext(fname)[1] diff --git a/src/calibre/devices/winusb.py b/src/calibre/devices/winusb.py index a7839e795a..406548878c 100644 --- a/src/calibre/devices/winusb.py +++ b/src/calibre/devices/winusb.py @@ -47,10 +47,10 @@ except ImportError: class GUID(Structure): _fields_ = [ - ("data1", DWORD), - ("data2", WORD), - ("data3", WORD), - ("data4", c_ubyte * 8)] + ('data1', DWORD), + ('data2', WORD), + ('data3', WORD), + ('data4', c_ubyte * 8)] def __init__(self, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8): self.data1 = l @@ -66,12 +66,12 @@ class GUID(Structure): self.data4[7] = b8 def __str__(self): - return "{{{:08x}-{:04x}-{:04x}-{}-{}}}".format( + return '{{{:08x}-{:04x}-{:04x}-{}-{}}}'.format( self.data1, self.data2, self.data3, - ''.join(["%02x" % d for d in self.data4[:2]]), - ''.join(["%02x" % d for d in self.data4[2:]]), + ''.join(['%02x' % d for d in self.data4[:2]]), + ''.join(['%02x' % d for d in self.data4[2:]]), ) @@ -130,7 +130,7 @@ class SP_DEVINFO_DATA(Structure): ] def __str__(self): - return f"ClassGuid:{self.ClassGuid} DevInst:{self.DevInst}" + return f'ClassGuid:{self.ClassGuid} DevInst:{self.DevInst}' PSP_DEVINFO_DATA = POINTER(SP_DEVINFO_DATA) @@ -145,7 +145,7 @@ class SP_DEVICE_INTERFACE_DATA(Structure): ] def __str__(self): - return f"InterfaceClassGuid:{self.InterfaceClassGuid} Flags:{self.Flags}" + return f'InterfaceClassGuid:{self.InterfaceClassGuid} Flags:{self.Flags}' ANYSIZE_ARRAY = 1 @@ -153,8 +153,8 @@ ANYSIZE_ARRAY = 1 class SP_DEVICE_INTERFACE_DETAIL_DATA(Structure): _fields_ = [ - ("cbSize", DWORD), - ("DevicePath", c_wchar*ANYSIZE_ARRAY) + ('cbSize', DWORD), + ('DevicePath', c_wchar*ANYSIZE_ARRAY) ] diff --git a/src/calibre/ebooks/chardet.py b/src/calibre/ebooks/chardet.py index 402d28bd3c..bb2a83ee9d 100644 --- a/src/calibre/ebooks/chardet.py +++ b/src/calibre/ebooks/chardet.py @@ -100,7 +100,7 @@ def find_declared_encoding(raw, limit=50*1024): return ans -_CHARSET_ALIASES = {"macintosh" : "mac-roman", "x-sjis" : "shift-jis", 'mac-centraleurope': 'cp1250'} +_CHARSET_ALIASES = {'macintosh' : 'mac-roman', 'x-sjis' : 'shift-jis', 'mac-centraleurope': 'cp1250'} def detect(bytestring): diff --git a/src/calibre/ebooks/chm/metadata.py b/src/calibre/ebooks/chm/metadata.py index 18736d2ecc..ced2008fb1 100644 --- a/src/calibre/ebooks/chm/metadata.py +++ b/src/calibre/ebooks/chm/metadata.py @@ -22,11 +22,11 @@ def _clean(s): def _detag(tag): - ans = "" + ans = '' if tag is None: return ans for elem in tag: - if hasattr(elem, "contents"): + if hasattr(elem, 'contents'): ans += _detag(elem) else: ans += _clean(elem) @@ -119,7 +119,7 @@ def _get_cover(soup, rdr): try: ans = rdr.GetFile(ans) except: - ans = rdr.root + "/" + ans + ans = rdr.root + '/' + ans try: ans = rdr.GetFile(ans) except: diff --git a/src/calibre/ebooks/chm/reader.py b/src/calibre/ebooks/chm/reader.py index 9bdd1a1c99..cf3c02b0e8 100644 --- a/src/calibre/ebooks/chm/reader.py +++ b/src/calibre/ebooks/chm/reader.py @@ -73,7 +73,7 @@ class CHMReader(CHMFile): # location of '.hhc' file, which is the CHM TOC. base = self.topics or self.home self.root = os.path.splitext(base.lstrip('/'))[0] - self.hhc_path = self.root + ".hhc" + self.hhc_path = self.root + '.hhc' def relpath_to_first_html_file(self): # See https://www.nongnu.org/chmspec/latest/Internal.html#SYSTEM @@ -170,10 +170,10 @@ class CHMReader(CHMFile): path = '/' + path res, ui = self.ResolveObject(path) if res != chmlib.CHM_RESOLVE_SUCCESS: - raise CHMError(f"Unable to locate {path!r} within CHM file {self.filename!r}") + raise CHMError(f'Unable to locate {path!r} within CHM file {self.filename!r}') size, data = self.RetrieveObject(ui) if size == 0: - raise CHMError(f"{path!r} is zero bytes in length!") + raise CHMError(f'{path!r} is zero bytes in length!') return data def get_home(self): @@ -254,7 +254,7 @@ class CHMReader(CHMFile): soup = BeautifulSoup(data) except ValueError: # hit some strange encoding problems... - self.log.exception("Unable to parse html for cleaning, leaving it") + self.log.exception('Unable to parse html for cleaning, leaving it') return data # nuke javascript... [s.extract() for s in soup('script')] diff --git a/src/calibre/ebooks/conversion/cli.py b/src/calibre/ebooks/conversion/cli.py index 0b43300c2e..beb3496656 100644 --- a/src/calibre/ebooks/conversion/cli.py +++ b/src/calibre/ebooks/conversion/cli.py @@ -151,10 +151,10 @@ def recipe_test(option, opt_str, value, parser): for arg in parser.rargs: # stop on --foo like options - if arg[:2] == "--": + if arg[:2] == '--': break # stop on -a, but not on -3 or -3.0 - if arg[:1] == "-" and len(arg) > 1 and not floatable(arg): + if arg[:1] == '-' and len(arg) > 1 and not floatable(arg): break try: value.append(int(arg)) diff --git a/src/calibre/ebooks/conversion/plugins/chm_input.py b/src/calibre/ebooks/conversion/plugins/chm_input.py index 4fc30bf8de..267e1725a4 100644 --- a/src/calibre/ebooks/conversion/plugins/chm_input.py +++ b/src/calibre/ebooks/conversion/plugins/chm_input.py @@ -132,7 +132,7 @@ class CHMInput(InputFormatPlugin): # print(etree.tostring(hhcroot, pretty_print=True)) # print("=============================") log.debug('Found %d section nodes' % toc.count()) - htmlpath = os.path.splitext(hhcpath)[0] + ".html" + htmlpath = os.path.splitext(hhcpath)[0] + '.html' base = os.path.dirname(os.path.abspath(htmlpath)) def unquote(x): diff --git a/src/calibre/ebooks/conversion/plugins/comic_input.py b/src/calibre/ebooks/conversion/plugins/comic_input.py index 08bfd6c437..aaa6df1cbf 100644 --- a/src/calibre/ebooks/conversion/plugins/comic_input.py +++ b/src/calibre/ebooks/conversion/plugins/comic_input.py @@ -44,8 +44,8 @@ class ComicInput(InputFormatPlugin): OptionRecommendation(name='landscape', recommended_value=False, help=_("Don't split landscape images into two portrait images")), OptionRecommendation(name='wide', recommended_value=False, - help=_("Keep aspect ratio and scale image using screen height as " - "image width for viewing in landscape mode.")), + help=_('Keep aspect ratio and scale image using screen height as ' + 'image width for viewing in landscape mode.')), OptionRecommendation(name='right2left', recommended_value=False, help=_('Used for right-to-left publications like manga. ' 'Causes landscape pages to be split into portrait pages ' @@ -62,7 +62,7 @@ class ComicInput(InputFormatPlugin): 'are converted to. You can experiment to see which format gives ' 'you optimal size and look on your device.')), OptionRecommendation(name='no_process', recommended_value=False, - help=_("Apply no processing to the image")), + help=_('Apply no processing to the image')), OptionRecommendation(name='dont_grayscale', recommended_value=False, help=_('Do not convert the image to grayscale (black and white)')), OptionRecommendation(name='comic_image_size', recommended_value=None, diff --git a/src/calibre/ebooks/conversion/plugins/djvu_input.py b/src/calibre/ebooks/conversion/plugins/djvu_input.py index dd38176da4..34587376ed 100644 --- a/src/calibre/ebooks/conversion/plugins/djvu_input.py +++ b/src/calibre/ebooks/conversion/plugins/djvu_input.py @@ -28,7 +28,7 @@ class DJVUInput(InputFormatPlugin): raise ValueError('The DJVU file contains no text, only images, probably page scans.' ' calibre only supports conversion of DJVU files with actual text in them.') - html = convert_basic(raw_text.replace(b"\n", b' ').replace( + html = convert_basic(raw_text.replace(b'\n', b' ').replace( b'\037', b'\n\n')) # Run the HTMLized text through the html processing plugin. from calibre.customize.ui import plugin_for_input_format diff --git a/src/calibre/ebooks/conversion/plugins/epub_input.py b/src/calibre/ebooks/conversion/plugins/epub_input.py index f919e64e34..a2404dfd8c 100644 --- a/src/calibre/ebooks/conversion/plugins/epub_input.py +++ b/src/calibre/ebooks/conversion/plugins/epub_input.py @@ -244,7 +244,7 @@ class EPUBInput(InputFormatPlugin): with open('META-INF/container.xml', 'rb') as f: root = safe_xml_fromstring(f.read()) for r in root.xpath('//*[local-name()="rootfile"]'): - if attr(r, 'media-type') != "application/oebps-package+xml": + if attr(r, 'media-type') != 'application/oebps-package+xml': continue path = attr(r, 'full-path') if not path: diff --git a/src/calibre/ebooks/conversion/plugins/epub_output.py b/src/calibre/ebooks/conversion/plugins/epub_output.py index 39ce9c7b4c..4662569008 100644 --- a/src/calibre/ebooks/conversion/plugins/epub_output.py +++ b/src/calibre/ebooks/conversion/plugins/epub_output.py @@ -77,7 +77,7 @@ class EPUBOutput(OutputFormatPlugin): ), OptionRecommendation(name='no_default_epub_cover', recommended_value=False, - help=_('Normally, if the input file has no cover and you don\'t' + help=_("Normally, if the input file has no cover and you don't" ' specify one, a default cover is generated with the title, ' 'authors, etc. This option disables the generation of this cover.') ), diff --git a/src/calibre/ebooks/conversion/plugins/fb2_input.py b/src/calibre/ebooks/conversion/plugins/fb2_input.py index e192c58071..c14d681735 100644 --- a/src/calibre/ebooks/conversion/plugins/fb2_input.py +++ b/src/calibre/ebooks/conversion/plugins/fb2_input.py @@ -1,8 +1,8 @@ __license__ = 'GPL v3' __copyright__ = '2008, Anatoly Shipitsin ' -""" +''' Convert .fb2 files to .lrf -""" +''' import os import re @@ -91,7 +91,7 @@ class FB2Input(InputFormatPlugin): log.debug('Converting XML to HTML...') with open(P('templates/fb2.xsl'), 'rb') as f: ss = f.read().decode('utf-8') - ss = ss.replace("__FB_NS__", fb_ns) + ss = ss.replace('__FB_NS__', fb_ns) if options.no_inline_fb2_toc: log('Disabling generation of inline FB2 TOC') ss = re.compile(r'.*', diff --git a/src/calibre/ebooks/conversion/plugins/html_output.py b/src/calibre/ebooks/conversion/plugins/html_output.py index 9a0a61ee85..ea64c70cb3 100644 --- a/src/calibre/ebooks/conversion/plugins/html_output.py +++ b/src/calibre/ebooks/conversion/plugins/html_output.py @@ -213,7 +213,7 @@ class HTMLOutput(OutputFormatPlugin): f.write(t.encode('utf-8')) item.unload_data_from_memory(memory=path) - zfile = zipfile.ZipFile(output_path, "w") + zfile = zipfile.ZipFile(output_path, 'w') zfile.add_dir(output_dir, basename(output_dir)) zfile.write(output_file, basename(output_file), zipfile.ZIP_DEFLATED) diff --git a/src/calibre/ebooks/conversion/plugins/htmlz_output.py b/src/calibre/ebooks/conversion/plugins/htmlz_output.py index 3c87c57492..98b5c6057b 100644 --- a/src/calibre/ebooks/conversion/plugins/htmlz_output.py +++ b/src/calibre/ebooks/conversion/plugins/htmlz_output.py @@ -39,7 +39,7 @@ class HTMLZOutput(OutputFormatPlugin): OptionRecommendation(name='htmlz_class_style', recommended_value='external', level=OptionRecommendation.LOW, choices=list(ui_data['sheet_choices']), - help=_('How to handle the CSS when using css-type = \'class\'.\n' + help=_("How to handle the CSS when using css-type = 'class'.\n" 'Default is external.\n' 'external: {external}\n' 'inline: {inline}' diff --git a/src/calibre/ebooks/conversion/plugins/lrf_output.py b/src/calibre/ebooks/conversion/plugins/lrf_output.py index b3d11d52a3..dfcc15dcb4 100644 --- a/src/calibre/ebooks/conversion/plugins/lrf_output.py +++ b/src/calibre/ebooks/conversion/plugins/lrf_output.py @@ -102,7 +102,7 @@ class LRFOutput(OutputFormatPlugin): OptionRecommendation(name='header', recommended_value=False, help=_('Add a header to all the pages with title and author.') ), - OptionRecommendation(name='header_format', recommended_value="%t by %a", + OptionRecommendation(name='header_format', recommended_value='%t by %a', help=_('Set the format of the header. %a is replaced by the author ' 'and %t by the title. Default is %default') ), diff --git a/src/calibre/ebooks/conversion/plugins/mobi_output.py b/src/calibre/ebooks/conversion/plugins/mobi_output.py index 15278d2838..cfe03aa79e 100644 --- a/src/calibre/ebooks/conversion/plugins/mobi_output.py +++ b/src/calibre/ebooks/conversion/plugins/mobi_output.py @@ -49,7 +49,7 @@ class MOBIOutput(OutputFormatPlugin): ), OptionRecommendation(name='no_inline_toc', recommended_value=False, level=OptionRecommendation.LOW, - help=_('Don\'t add Table of Contents to the book. Useful if ' + help=_("Don't add Table of Contents to the book. Useful if " 'the book has its own table of contents.')), OptionRecommendation(name='toc_title', recommended_value=None, help=_('Title for any generated inline table of contents.') @@ -280,7 +280,7 @@ class AZW3Output(OutputFormatPlugin): ), OptionRecommendation(name='no_inline_toc', recommended_value=False, level=OptionRecommendation.LOW, - help=_('Don\'t add Table of Contents to the book. Useful if ' + help=_("Don't add Table of Contents to the book. Useful if " 'the book has its own table of contents.')), OptionRecommendation(name='toc_title', recommended_value=None, help=_('Title for any generated inline table of contents.') diff --git a/src/calibre/ebooks/conversion/plugins/snb_input.py b/src/calibre/ebooks/conversion/plugins/snb_input.py index 7fbb2f773a..19da8ae2f9 100644 --- a/src/calibre/ebooks/conversion/plugins/snb_input.py +++ b/src/calibre/ebooks/conversion/plugins/snb_input.py @@ -33,16 +33,16 @@ class SNBInput(InputFormatPlugin): from calibre.ebooks.snb.snbfile import SNBFile from calibre.utils.xml_parse import safe_xml_fromstring - log.debug("Parsing SNB file...") + log.debug('Parsing SNB file...') snbFile = SNBFile() try: snbFile.Parse(stream) except: - raise ValueError("Invalid SNB file") + raise ValueError('Invalid SNB file') if not snbFile.IsValid(): - log.debug("Invalid SNB file") - raise ValueError("Invalid SNB file") - log.debug("Handle meta data ...") + log.debug('Invalid SNB file') + raise ValueError('Invalid SNB file') + log.debug('Handle meta data ...') from calibre.ebooks.conversion.plumber import create_oebbook oeb = create_oebbook(log, None, options, encoding=options.input_encoding, populate=False) diff --git a/src/calibre/ebooks/conversion/plugins/snb_output.py b/src/calibre/ebooks/conversion/plugins/snb_output.py index 3a74b634f9..cfffbde523 100644 --- a/src/calibre/ebooks/conversion/plugins/snb_output.py +++ b/src/calibre/ebooks/conversion/plugins/snb_output.py @@ -97,27 +97,27 @@ class SNBOutput(OutputFormatPlugin): href = g['cover'].href # Output book info file - bookInfoTree = etree.Element("book-snbf", version="1.0") - headTree = etree.SubElement(bookInfoTree, "head") - etree.SubElement(headTree, "name").text = title - etree.SubElement(headTree, "author").text = ' '.join(authors) - etree.SubElement(headTree, "language").text = lang - etree.SubElement(headTree, "rights") - etree.SubElement(headTree, "publisher").text = publishers - etree.SubElement(headTree, "generator").text = __appname__ + ' ' + __version__ - etree.SubElement(headTree, "created") - etree.SubElement(headTree, "abstract").text = abstract + bookInfoTree = etree.Element('book-snbf', version='1.0') + headTree = etree.SubElement(bookInfoTree, 'head') + etree.SubElement(headTree, 'name').text = title + etree.SubElement(headTree, 'author').text = ' '.join(authors) + etree.SubElement(headTree, 'language').text = lang + etree.SubElement(headTree, 'rights') + etree.SubElement(headTree, 'publisher').text = publishers + etree.SubElement(headTree, 'generator').text = __appname__ + ' ' + __version__ + etree.SubElement(headTree, 'created') + etree.SubElement(headTree, 'abstract').text = abstract if href is not None: - etree.SubElement(headTree, "cover").text = ProcessFileName(href) + etree.SubElement(headTree, 'cover').text = ProcessFileName(href) else: - etree.SubElement(headTree, "cover") + etree.SubElement(headTree, 'cover') with open(os.path.join(snbfDir, 'book.snbf'), 'wb') as f: f.write(etree.tostring(bookInfoTree, pretty_print=True, encoding='utf-8')) # Output TOC - tocInfoTree = etree.Element("toc-snbf") - tocHead = etree.SubElement(tocInfoTree, "head") - tocBody = etree.SubElement(tocInfoTree, "body") + tocInfoTree = etree.Element('toc-snbf') + tocHead = etree.SubElement(tocInfoTree, 'head') + tocBody = etree.SubElement(tocInfoTree, 'body') outputFiles = {} if oeb_book.toc.count() == 0: log.warn('This SNB file has no Table of Contents. ' @@ -131,11 +131,11 @@ class SNBOutput(OutputFormatPlugin): # "Cover Pages". # oeb_book.toc does not support "insert", so we generate # the tocInfoTree directly instead of modifying the toc - ch = etree.SubElement(tocBody, "chapter") - ch.set("src", ProcessFileName(first.href) + ".snbc") + ch = etree.SubElement(tocBody, 'chapter') + ch.set('src', ProcessFileName(first.href) + '.snbc') ch.text = _('Cover pages') outputFiles[first.href] = [] - outputFiles[first.href].append(("", _("Cover pages"))) + outputFiles[first.href].append(('', _('Cover pages'))) for tocitem in oeb_book.toc: if tocitem.href.find('#') != -1: @@ -147,23 +147,23 @@ class SNBOutput(OutputFormatPlugin): outputFiles[item[0]].append((item[1], tocitem.title)) else: outputFiles[item[0]] = [] - if "" not in outputFiles[item[0]]: - outputFiles[item[0]].append(("", tocitem.title + _(" (Preface)"))) - ch = etree.SubElement(tocBody, "chapter") - ch.set("src", ProcessFileName(item[0]) + ".snbc") - ch.text = tocitem.title + _(" (Preface)") + if '' not in outputFiles[item[0]]: + outputFiles[item[0]].append(('', tocitem.title + _(' (Preface)'))) + ch = etree.SubElement(tocBody, 'chapter') + ch.set('src', ProcessFileName(item[0]) + '.snbc') + ch.text = tocitem.title + _(' (Preface)') outputFiles[item[0]].append((item[1], tocitem.title)) else: if tocitem.href in outputFiles: - outputFiles[tocitem.href].append(("", tocitem.title)) + outputFiles[tocitem.href].append(('', tocitem.title)) else: outputFiles[tocitem.href] = [] - outputFiles[tocitem.href].append(("", tocitem.title)) - ch = etree.SubElement(tocBody, "chapter") - ch.set("src", ProcessFileName(tocitem.href) + ".snbc") + outputFiles[tocitem.href].append(('', tocitem.title)) + ch = etree.SubElement(tocBody, 'chapter') + ch.set('src', ProcessFileName(tocitem.href) + '.snbc') ch.text = tocitem.title - etree.SubElement(tocHead, "chapters").text = '%d' % len(tocBody) + etree.SubElement(tocHead, 'chapters').text = '%d' % len(tocBody) with open(os.path.join(snbfDir, 'toc.snbf'), 'wb') as f: f.write(etree.tostring(tocInfoTree, pretty_print=True, encoding='utf-8')) @@ -194,13 +194,13 @@ class SNBOutput(OutputFormatPlugin): postfix = '' if subName != '': postfix = '_' + subName - lastName = ProcessFileName(item.href + postfix + ".snbc") + lastName = ProcessFileName(item.href + postfix + '.snbc') oldTree = snbcTrees[subName] with open(os.path.join(snbcDir, lastName), 'wb') as f: f.write(etree.tostring(oldTree, pretty_print=True, encoding='utf-8')) else: log.debug('Merge %s with last TOC item...' % item.href) - snbwriter.merge_content(oldTree, oeb_book, item, [('', _("Start"))], opts) + snbwriter.merge_content(oldTree, oeb_book, item, [('', _('Start'))], opts) # Output the last one if needed log.debug('Output the last modified chapter again: %s' % lastName) diff --git a/src/calibre/ebooks/conversion/plugins/txt_input.py b/src/calibre/ebooks/conversion/plugins/txt_input.py index 048a02c316..c4d620720d 100644 --- a/src/calibre/ebooks/conversion/plugins/txt_input.py +++ b/src/calibre/ebooks/conversion/plugins/txt_input.py @@ -21,7 +21,7 @@ MD_EXTENSIONS = { 'meta': _('Metadata in the document'), 'nl2br': _('Treat newlines as hard breaks'), 'sane_lists': _('Do not allow mixing list types'), - 'smarty': _('Use Markdown\'s internal smartypants parser'), + 'smarty': _("Use Markdown's internal smartypants parser"), 'tables': _('Support tables'), 'toc': _('Generate a table of contents'), 'wikilinks': _('Wiki style links'), @@ -43,7 +43,7 @@ class TXTInput(InputFormatPlugin): 'single': _('Assume every line is a paragraph'), 'print': _('Assume every line starting with 2+ spaces or a tab starts a paragraph'), 'unformatted': _('Most lines have hard line breaks, few/no blank lines or indents'), - 'off': _('Don\'t modify the paragraph structure'), + 'off': _("Don't modify the paragraph structure"), }, 'formatting_types': { 'auto': _('Automatically decide which formatting processor to use'), @@ -83,7 +83,7 @@ class TXTInput(InputFormatPlugin): OptionRecommendation(name='txt_in_remove_indents', recommended_value=False, help=_('Normally extra space at the beginning of lines is retained. ' 'With this option they will be removed.')), - OptionRecommendation(name="markdown_extensions", recommended_value='footnotes, tables, toc', + OptionRecommendation(name='markdown_extensions', recommended_value='footnotes, tables, toc', help=_('Enable extensions to Markdown syntax. Extensions are formatting that is not part ' 'of the standard Markdown format. The extensions enabled by default: %default.\n' 'To learn more about Markdown extensions, see {}\n' diff --git a/src/calibre/ebooks/conversion/plugins/txt_output.py b/src/calibre/ebooks/conversion/plugins/txt_output.py index d90ff6d802..faea8f6b56 100644 --- a/src/calibre/ebooks/conversion/plugins/txt_output.py +++ b/src/calibre/ebooks/conversion/plugins/txt_output.py @@ -30,9 +30,9 @@ class TXTOutput(OutputFormatPlugin): OptionRecommendation(name='newline', recommended_value='system', level=OptionRecommendation.LOW, short_switch='n', choices=NEWLINE_TYPES, - help=_('Type of newline to use. Options are %s. Default is \'system\'. ' - 'Use \'old_mac\' for compatibility with Mac OS 9 and earlier. ' - 'For macOS use \'unix\'. \'system\' will default to the newline ' + help=_("Type of newline to use. Options are %s. Default is 'system'. " + "Use 'old_mac' for compatibility with Mac OS 9 and earlier. " + "For macOS use 'unix'. 'system' will default to the newline " 'type used by this OS.') % sorted(NEWLINE_TYPES)), OptionRecommendation(name='txt_output_encoding', recommended_value='utf-8', level=OptionRecommendation.LOW, diff --git a/src/calibre/ebooks/conversion/plumber.py b/src/calibre/ebooks/conversion/plumber.py index 69dcdcf9ec..9f9699724c 100644 --- a/src/calibre/ebooks/conversion/plumber.py +++ b/src/calibre/ebooks/conversion/plumber.py @@ -187,7 +187,7 @@ OptionRecommendation(name='disable_font_rescaling', OptionRecommendation(name='minimum_line_height', recommended_value=120.0, level=OptionRecommendation.LOW, help=_( - 'The minimum line height, as a percentage of the element\'s ' + "The minimum line height, as a percentage of the element's " 'calculated font size. calibre will ensure that every element ' 'has a line height of at least this setting, irrespective of ' 'what the input document specifies. Set to zero to disable. ' diff --git a/src/calibre/ebooks/conversion/preprocess.py b/src/calibre/ebooks/conversion/preprocess.py index 83a8a9dadd..bd843c082b 100644 --- a/src/calibre/ebooks/conversion/preprocess.py +++ b/src/calibre/ebooks/conversion/preprocess.py @@ -200,8 +200,8 @@ class Dehyphenator: "((ed)?ly|'?e?s||a?(t|s)?ion(s|al(ly)?)?|ings?|er|(i)?ous|" "(i|a)ty|(it)?ies|ive|gence|istic(ally)?|(e|a)nce|m?ents?|ism|ated|" "(e|u)ct(ed)?|ed|(i|ed)?ness|(e|a)ncy|ble|ier|al|ex|ian)$") - self.suffixes = re.compile(r"^%s" % self.suffix_string, re.IGNORECASE) - self.removesuffixes = re.compile(r"%s" % self.suffix_string, re.IGNORECASE) + self.suffixes = re.compile(r'^%s' % self.suffix_string, re.IGNORECASE) + self.removesuffixes = re.compile(r'%s' % self.suffix_string, re.IGNORECASE) # remove prefixes if the prefix was not already the point of hyphenation self.prefix_string = '^(dis|re|un|in|ex)' self.prefixes = re.compile(r'%s$' % self.prefix_string, re.IGNORECASE) @@ -214,7 +214,7 @@ class Dehyphenator: wraptags = match.group('wraptags') except: wraptags = '' - hyphenated = str(firsthalf) + "-" + str(secondhalf) + hyphenated = str(firsthalf) + '-' + str(secondhalf) dehyphenated = str(firsthalf) + str(secondhalf) if self.suffixes.match(secondhalf) is None: lookupword = self.removesuffixes.sub('', dehyphenated) @@ -223,7 +223,7 @@ class Dehyphenator: if len(firsthalf) > 4 and self.prefixes.match(firsthalf) is None: lookupword = self.removeprefix.sub('', lookupword) if self.verbose > 2: - self.log("lookup word is: "+lookupword+", orig is: " + hyphenated) + self.log('lookup word is: '+lookupword+', orig is: ' + hyphenated) try: searchresult = self.html.find(lookupword.lower()) except: @@ -231,33 +231,33 @@ class Dehyphenator: if self.format == 'html_cleanup' or self.format == 'txt_cleanup': if self.html.find(lookupword) != -1 or searchresult != -1: if self.verbose > 2: - self.log(" Cleanup:returned dehyphenated word: " + dehyphenated) + self.log(' Cleanup:returned dehyphenated word: ' + dehyphenated) return dehyphenated elif self.html.find(hyphenated) != -1: if self.verbose > 2: - self.log(" Cleanup:returned hyphenated word: " + hyphenated) + self.log(' Cleanup:returned hyphenated word: ' + hyphenated) return hyphenated else: if self.verbose > 2: - self.log(" Cleanup:returning original text "+firsthalf+" + linefeed "+secondhalf) + self.log(' Cleanup:returning original text '+firsthalf+' + linefeed '+secondhalf) return firsthalf+'\u2014'+wraptags+secondhalf else: if self.format == 'individual_words' and len(firsthalf) + len(secondhalf) <= 6: if self.verbose > 2: - self.log("too short, returned hyphenated word: " + hyphenated) + self.log('too short, returned hyphenated word: ' + hyphenated) return hyphenated if len(firsthalf) <= 2 and len(secondhalf) <= 2: if self.verbose > 2: - self.log("too short, returned hyphenated word: " + hyphenated) + self.log('too short, returned hyphenated word: ' + hyphenated) return hyphenated if self.html.find(lookupword) != -1 or searchresult != -1: if self.verbose > 2: - self.log(" returned dehyphenated word: " + dehyphenated) + self.log(' returned dehyphenated word: ' + dehyphenated) return dehyphenated else: if self.verbose > 2: - self.log(" returned hyphenated word: " + hyphenated) + self.log(' returned hyphenated word: ' + hyphenated) return hyphenated def __call__(self, html, format, length=1): @@ -461,7 +461,7 @@ class HTMLPreProcessor: return re.search('<]*id=BookTitle', raw) is not None def is_pdftohtml(self, src): - return '' in src[:1000] + return "" in src[:1000] def __call__(self, html, remove_special_chars=None, get_preprocess_html=False): @@ -617,7 +617,7 @@ class HTMLPreProcessor: html = preprocessor(html) if is_pdftohtml: - html = html.replace('', '') + html = html.replace("", '') if getattr(self.extra_opts, 'smarten_punctuation', False): html = smarten_punctuation(html, self.log) diff --git a/src/calibre/ebooks/conversion/utils.py b/src/calibre/ebooks/conversion/utils.py index 5c074a69f1..915ff89d05 100644 --- a/src/calibre/ebooks/conversion/utils.py +++ b/src/calibre/ebooks/conversion/utils.py @@ -33,16 +33,16 @@ class HeuristicProcessor: self.multi_blank = re.compile(r'(\s*]*>\s*

(\s*]*>\s*
\s*)*){2,}(?!\s*]*>\s*

(\s*]*>\s*
\s*)*){2,}', re.IGNORECASE) self.line_open = ( - r"<(?Pp|div)[^>]*>\s*(<(?Pfont|span|[ibu])[^>]*>)?\s*" - r"(<(?Pfont|span|[ibu])[^>]*>)?\s*(<(?Pfont|span|[ibu])[^>]*>)?\s*") - self.line_close = "()?\\s*()?\\s*()?\\s*" + r'<(?Pp|div)[^>]*>\s*(<(?Pfont|span|[ibu])[^>]*>)?\s*' + r'(<(?Pfont|span|[ibu])[^>]*>)?\s*(<(?Pfont|span|[ibu])[^>]*>)?\s*') + self.line_close = '()?\\s*()?\\s*()?\\s*' self.single_blank = re.compile(r'(\s*<(p|div)[^>]*>\s*)', re.IGNORECASE) self.scene_break_open = '

' - self.common_in_text_endings = '[\"\'—’”,\\.!\\?\\…\\)„\\w]' - self.common_in_text_beginnings = '[\\w\'\"“‘‛]' + self.common_in_text_endings = '["\'—’”,\\.!\\?\\…\\)„\\w]' + self.common_in_text_beginnings = '[\\w\'"“‘‛]' def is_pdftohtml(self, src): - return '' in src[:1000] + return "" in src[:1000] def is_abbyy(self, src): return ''+chap+'\n' else: delete_whitespace = re.compile('^\\s*(?P.*?)\\s*$') - delete_quotes = re.compile('\'\"') + delete_quotes = re.compile('\'"') txt_chap = delete_quotes.sub('', delete_whitespace.sub('\\g', html2text(chap))) txt_title = delete_quotes.sub('', delete_whitespace.sub('\\g', html2text(title))) self.html_preprocess_sections = self.html_preprocess_sections + 1 - self.log.debug("marked " + str(self.html_preprocess_sections) + - " chapters & titles. - " + str(chap) + ", " + str(title)) + self.log.debug('marked ' + str(self.html_preprocess_sections) + + ' chapters & titles. - ' + str(chap) + ', ' + str(title)) return '

'+chap+'

\n

'+title+'

\n' def chapter_break(self, match): chap = match.group('section') styles = match.group('styles') self.html_preprocess_sections = self.html_preprocess_sections + 1 - self.log.debug("marked " + str(self.html_preprocess_sections) + - " section markers based on punctuation. - " + str(chap)) + self.log.debug('marked ' + str(self.html_preprocess_sections) + + ' section markers based on punctuation. - ' + str(chap)) return '<'+styles+' style="page-break-before:always">'+chap def analyze_title_matches(self, match): @@ -208,59 +208,59 @@ class HeuristicProcessor: if wordcount > 200000: typical_chapters = 15000. self.min_chapters = int(ceil(wordcount / typical_chapters)) - self.log.debug("minimum chapters required are: "+str(self.min_chapters)) + self.log.debug('minimum chapters required are: '+str(self.min_chapters)) heading = re.compile(']*>', re.IGNORECASE) self.html_preprocess_sections = len(heading.findall(html)) - self.log.debug("found " + str(self.html_preprocess_sections) + " pre-existing headings") + self.log.debug('found ' + str(self.html_preprocess_sections) + ' pre-existing headings') # Build the Regular Expressions in pieces - init_lookahead = "(?=<(p|div))" + init_lookahead = '(?=<(p|div))' chapter_line_open = self.line_open - title_line_open = (r"<(?Pp|div)[^>]*>\s*(<(?Pfont|span|[ibu])[^>]*>)?" - r"\s*(<(?Pfont|span|[ibu])[^>]*>)?\s*(<(?Pfont|span|[ibu])[^>]*>)?\s*") - chapter_header_open = r"(?P" - title_header_open = r"(?P" - chapter_header_close = ")\\s*" - title_header_close = ")" + title_line_open = (r'<(?P<outer2>p|div)[^>]*>\s*(<(?P<inner4>font|span|[ibu])[^>]*>)?' + r'\s*(<(?P<inner5>font|span|[ibu])[^>]*>)?\s*(<(?P<inner6>font|span|[ibu])[^>]*>)?\s*') + chapter_header_open = r'(?P<chap>' + title_header_open = r'(?P<title>' + chapter_header_close = ')\\s*' + title_header_close = ')' chapter_line_close = self.line_close - title_line_close = "(</(?P=inner6)>)?\\s*(</(?P=inner5)>)?\\s*(</(?P=inner4)>)?\\s*</(?P=outer2)>" + title_line_close = '(</(?P=inner6)>)?\\s*(</(?P=inner5)>)?\\s*(</(?P=inner4)>)?\\s*</(?P=outer2)>' is_pdftohtml = self.is_pdftohtml(html) if is_pdftohtml: - title_line_open = "<(?P<outer2>p)[^>]*>\\s*" - title_line_close = "\\s*</(?P=outer2)>" + title_line_open = '<(?P<outer2>p)[^>]*>\\s*' + title_line_close = '\\s*</(?P=outer2)>' if blanks_between_paragraphs: - blank_lines = "(\\s*<p[^>]*>\\s*</p>){0,2}\\s*" + blank_lines = '(\\s*<p[^>]*>\\s*</p>){0,2}\\s*' else: - blank_lines = "" - opt_title_open = "(" - opt_title_close = ")?" - n_lookahead_open = "(?!\\s*" - n_lookahead_close = ")\\s*" + blank_lines = '' + opt_title_open = '(' + opt_title_close = ')?' + n_lookahead_open = '(?!\\s*' + n_lookahead_close = ')\\s*' default_title = r"(<[ibu][^>]*>)?\s{0,3}(?!Chapter)([\w\:\'’\"-]+\s{0,3}){1,5}?(</[ibu][^>]*>)?(?=<)" - simple_title = r"(<[ibu][^>]*>)?\s{0,3}(?!(Chapter|\s+<)).{0,65}?(</[ibu][^>]*>)?(?=<)" + simple_title = r'(<[ibu][^>]*>)?\s{0,3}(?!(Chapter|\s+<)).{0,65}?(</[ibu][^>]*>)?(?=<)' analysis_result = [] chapter_types = [ [( r"[^'\"]?(Introduction|Synopsis|Acknowledgements|Epilogue|CHAPTER|Kapitel|Volume\b|Prologue|Book\b|Part\b|Dedication|Preface)" - r"\s*([\d\w-]+\:?\'?\s*){0,5}"), True, True, True, False, "Searching for common section headings", 'common'], + r"\s*([\d\w-]+\:?\'?\s*){0,5}"), True, True, True, False, 'Searching for common section headings', 'common'], # Highest frequency headings which include titles - [r"[^'\"]?(CHAPTER|Kapitel)\s*([\dA-Z\-\'\"\?!#,]+\s*){0,7}\s*", True, True, True, False, "Searching for most common chapter headings", 'chapter'], - [r"<b[^>]*>\s*(<span[^>]*>)?\s*(?!([*#•=]+\s*)+)(\s*(?=[\d.\w#\-*\s]+<)([\d.\w#-*]+\s*){1,5}\s*)(?!\.)(</span>)?\s*</b>", - True, True, True, False, "Searching for emphasized lines", 'emphasized'], # Emphasized lines + [r"[^'\"]?(CHAPTER|Kapitel)\s*([\dA-Z\-\'\"\?!#,]+\s*){0,7}\s*", True, True, True, False, 'Searching for most common chapter headings', 'chapter'], + [r'<b[^>]*>\s*(<span[^>]*>)?\s*(?!([*#•=]+\s*)+)(\s*(?=[\d.\w#\-*\s]+<)([\d.\w#-*]+\s*){1,5}\s*)(?!\.)(</span>)?\s*</b>', + True, True, True, False, 'Searching for emphasized lines', 'emphasized'], # Emphasized lines [r"[^'\"]?(\d+(\.|:))\s*([\w\-\'\"#,]+\s*){0,7}\s*", True, True, True, False, - "Searching for numeric chapter headings", 'numeric'], # Numeric Chapters - [r"([A-Z]\s+){3,}\s*([\d\w-]+\s*){0,3}\s*", True, True, True, False, "Searching for letter spaced headings", 'letter_spaced'], # Spaced Lettering + 'Searching for numeric chapter headings', 'numeric'], # Numeric Chapters + [r'([A-Z]\s+){3,}\s*([\d\w-]+\s*){0,3}\s*', True, True, True, False, 'Searching for letter spaced headings', 'letter_spaced'], # Spaced Lettering [r"[^'\"]?(\d+\.?\s+([\d\w-]+\:?\'?-?\s?){0,5})\s*", True, True, True, False, - "Searching for numeric chapters with titles", 'numeric_title'], # Numeric Titles + 'Searching for numeric chapters with titles', 'numeric_title'], # Numeric Titles [r"[^'\"]?(\d+)\s*([\dA-Z\-\'\"\?!#,]+\s*){0,7}\s*", True, True, True, False, - "Searching for simple numeric headings", 'plain_number'], # Numeric Chapters, no dot or colon + 'Searching for simple numeric headings', 'plain_number'], # Numeric Chapters, no dot or colon [r"\s*[^'\"]?([A-Z#]+(\s|-){0,3}){1,5}\s*", False, True, False, False, - "Searching for chapters with Uppercase Characters", 'uppercase'] # Uppercase Chapters + 'Searching for chapters with Uppercase Characters', 'uppercase'] # Uppercase Chapters ] def recurse_patterns(html, analyze): @@ -299,9 +299,9 @@ class HeuristicProcessor: break full_chapter_line = chapter_line_open+chapter_header_open+chapter_type+chapter_header_close+chapter_line_close if n_lookahead_req: - n_lookahead = re.sub("(ou|in|cha)", "lookahead_", full_chapter_line) + n_lookahead = re.sub('(ou|in|cha)', 'lookahead_', full_chapter_line) if not analyze: - self.log.debug("Marked " + str(self.html_preprocess_sections) + " headings, " + log_message) + self.log.debug('Marked ' + str(self.html_preprocess_sections) + ' headings, ' + log_message) chapter_marker = arg_ignorecase+init_lookahead+full_chapter_line+blank_lines+lp_n_lookahead_open+n_lookahead+lp_n_lookahead_close+ \ lp_opt_title_open+title_line_open+title_header_open+lp_title+title_header_close+title_line_close+lp_opt_title_close @@ -315,10 +315,10 @@ class HeuristicProcessor: title_req = True strict_title = False self.log.debug( - str(type_name)+" had "+str(hits)+ - " hits - "+str(self.chapters_no_title)+" chapters with no title, "+ - str(self.chapters_with_title)+" chapters with titles, "+ - str(float(self.chapters_with_title) / float(hits))+" percent. ") + str(type_name)+' had '+str(hits)+ + ' hits - '+str(self.chapters_no_title)+' chapters with no title, '+ + str(self.chapters_with_title)+' chapters with titles, '+ + str(float(self.chapters_with_title) / float(hits))+' percent. ') if type_name == 'common': analysis_result.append([chapter_type, n_lookahead_req, strict_title, ignorecase, title_req, log_message, type_name]) elif self.min_chapters <= hits < max_chapters or self.min_chapters < 3 > hits: @@ -335,8 +335,8 @@ class HeuristicProcessor: words_per_chptr = wordcount if words_per_chptr > 0 and self.html_preprocess_sections > 0: words_per_chptr = wordcount // self.html_preprocess_sections - self.log.debug("Total wordcount is: "+ str(wordcount)+", Average words per section is: "+ - str(words_per_chptr)+", Marked up "+str(self.html_preprocess_sections)+" chapters") + self.log.debug('Total wordcount is: '+ str(wordcount)+', Average words per section is: '+ + str(words_per_chptr)+', Marked up '+str(self.html_preprocess_sections)+' chapters') return html def punctuation_unwrap(self, length, content, format): @@ -366,13 +366,13 @@ class HeuristicProcessor: # define the pieces of the regex # (?<!\&\w{4});) is a semicolon not part of an entity - lookahead = "(?<=.{"+str(length)+r"}([a-zა-ჰäëïöüàèìòùáćéíĺóŕńśúýźâêîôûçąężłıãõñæøþðßěľščťžňďřůёђєіїјљњћўџѣа-я,:)\\IAß]|(?<!\&\w{4});))" - em_en_lookahead = "(?<=.{"+str(length)+"}[\u2013\u2014])" - soft_hyphen = "\xad" - line_ending = "\\s*(?P<style_close></(span|[iub])>)?\\s*(</(p|div)>)?" - blanklines = "\\s*(?P<up2threeblanks><(p|span|div)[^>]*>\\s*(<(p|span|div)[^>]*>\\s*</(span|p|div)>\\s*)</(span|p|div)>\\s*){0,3}\\s*" - line_opening = "<(p|div)[^>]*>\\s*(?P<style_open><(span|[iub])[^>]*>)?\\s*" - txt_line_wrap = "((\u0020|\u0009)*\n){1,4}" + lookahead = '(?<=.{'+str(length)+r'}([a-zა-ჰäëïöüàèìòùáćéíĺóŕńśúýźâêîôûçąężłıãõñæøþðßěľščťžňďřůёђєіїјљњћўџѣа-я,:)\\IAß]|(?<!\&\w{4});))' + em_en_lookahead = '(?<=.{'+str(length)+'}[\u2013\u2014])' + soft_hyphen = '\xad' + line_ending = '\\s*(?P<style_close></(span|[iub])>)?\\s*(</(p|div)>)?' + blanklines = '\\s*(?P<up2threeblanks><(p|span|div)[^>]*>\\s*(<(p|span|div)[^>]*>\\s*</(span|p|div)>\\s*)</(span|p|div)>\\s*){0,3}\\s*' + line_opening = '<(p|div)[^>]*>\\s*(?P<style_open><(span|[iub])[^>]*>)?\\s*' + txt_line_wrap = '((\u0020|\u0009)*\n){1,4}' if format == 'txt': unwrap_regex = lookahead+txt_line_wrap @@ -383,9 +383,9 @@ class HeuristicProcessor: em_en_unwrap_regex = em_en_lookahead+line_ending+blanklines+line_opening shy_unwrap_regex = soft_hyphen+line_ending+blanklines+line_opening - unwrap = re.compile("%s" % unwrap_regex, re.UNICODE) - em_en_unwrap = re.compile("%s" % em_en_unwrap_regex, re.UNICODE) - shy_unwrap = re.compile("%s" % shy_unwrap_regex, re.UNICODE) + unwrap = re.compile('%s' % unwrap_regex, re.UNICODE) + em_en_unwrap = re.compile('%s' % em_en_unwrap_regex, re.UNICODE) + shy_unwrap = re.compile('%s' % shy_unwrap_regex, re.UNICODE) if format == 'txt': content = unwrap.sub(' ', content) @@ -408,7 +408,7 @@ class HeuristicProcessor: def markup_pre(self, html): pre = re.compile(r'<pre>', re.IGNORECASE) if len(pre.findall(html)) >= 1: - self.log.debug("Running Text Processing") + self.log.debug('Running Text Processing') outerhtml = re.compile(r'.*?(?<=<pre>)(?P<text>.*?)</pre>', re.IGNORECASE|re.DOTALL) html = outerhtml.sub(self.txt_process, html) from calibre.ebooks.conversion.preprocess import convert_entities @@ -422,15 +422,15 @@ class HeuristicProcessor: return html def arrange_htm_line_endings(self, html): - html = re.sub(r"\s*</(?P<tag>p|div)>", "</"+"\\g<tag>"+">\n", html) - html = re.sub(r"\s*<(?P<tag>p|div)(?P<style>[^>]*)>\s*", "\n<"+"\\g<tag>"+"\\g<style>"+">", html) + html = re.sub(r'\s*</(?P<tag>p|div)>', '</'+'\\g<tag>'+'>\n', html) + html = re.sub(r'\s*<(?P<tag>p|div)(?P<style>[^>]*)>\s*', '\n<'+'\\g<tag>'+'\\g<style>'+'>', html) return html def fix_nbsp_indents(self, html): txtindent = re.compile(r'<(?P<tagtype>p|div)(?P<formatting>[^>]*)>\s*(?P<span>(<span[^>]*>\s*)+)?\s*(\u00a0){2,}', re.IGNORECASE) html = txtindent.sub(self.insert_indent, html) if self.found_indents > 1: - self.log.debug("replaced "+str(self.found_indents)+ " nbsp indents with inline styles") + self.log.debug('replaced '+str(self.found_indents)+ ' nbsp indents with inline styles') return html def cleanup_markup(self, html): @@ -447,9 +447,9 @@ class HeuristicProcessor: fmt_tags = 'font|[ibu]|em|strong' open_fmt_pat, close_fmt_pat = fr'<(?:{fmt_tags})(?:\s[^>]*)?>', f'</(?:{fmt_tags})>' for i in range(2): - html = re.sub(r"\s*<span[^>]*>\s*(<span[^>]*>\s*</span>){0,2}\s*</span>\s*", " ", html) + html = re.sub(r'\s*<span[^>]*>\s*(<span[^>]*>\s*</span>){0,2}\s*</span>\s*', ' ', html) html = re.sub( - r"\s*{open}\s*({open}\s*{close}\s*){{0,2}}\s*{close}".format(open=open_fmt_pat, close=close_fmt_pat) , " ", html) + r'\s*{open}\s*({open}\s*{close}\s*){{0,2}}\s*{close}'.format(open=open_fmt_pat, close=close_fmt_pat) , ' ', html) # delete surrounding divs from empty paragraphs html = re.sub('<div[^>]*>\\s*<p[^>]*>\\s*</p>\\s*</div>', '<p> </p>', html) # Empty heading tags @@ -478,8 +478,8 @@ class HeuristicProcessor: blanklines = self.blankreg.findall(html) lines = self.linereg.findall(html) if len(lines) > 1: - self.log.debug("There are " + str(len(blanklines)) + " blank lines. " + - str(float(len(blanklines)) / float(len(lines))) + " percent blank") + self.log.debug('There are ' + str(len(blanklines)) + ' blank lines. ' + + str(float(len(blanklines)) / float(len(lines))) + ' percent blank') if float(len(blanklines)) / float(len(lines)) > 0.40: return True @@ -627,14 +627,14 @@ class HeuristicProcessor: def check_paragraph(self, content): content = re.sub('\\s*</?span[^>]*>\\s*', '', content) - if re.match('.*[\"\'.!?:]$', content): + if re.match('.*["\'.!?:]$', content): # print "detected this as a paragraph" return True else: return False def abbyy_processor(self, html): - abbyy_line = re.compile('((?P<linestart><p\\sstyle="(?P<styles>[^\"]*?);?">)(?P<content>.*?)(?P<lineend></p>)|(?P<image><img[^>]*>))', re.IGNORECASE) + abbyy_line = re.compile('((?P<linestart><p\\sstyle="(?P<styles>[^"]*?);?">)(?P<content>.*?)(?P<lineend></p>)|(?P<image><img[^>]*>))', re.IGNORECASE) empty_paragraph = '\n<p> </p>\n' self.in_blockquote = False self.previous_was_paragraph = False @@ -714,7 +714,7 @@ class HeuristicProcessor: # print "\n***\nline is:\n "+str(match.group(0))+'\n' if debugabby: # print "this line is a paragraph = "+str(is_paragraph)+", previous line was "+str(self.previous_was_paragraph) - self.log.debug("styles for this line were:", styles) + self.log.debug('styles for this line were:', styles) self.log.debug('newline is:') self.log.debug(blockquote_open_loop+blockquote_close_loop+ paragraph_before+'<p style="'+text_indent+text_align+ @@ -728,7 +728,7 @@ class HeuristicProcessor: return html def __call__(self, html): - self.log.debug("********* Heuristic processing HTML *********") + self.log.debug('********* Heuristic processing HTML *********') # Count the words in the document to estimate how many chapters to look for and whether # other types of processing are attempted try: @@ -737,7 +737,7 @@ class HeuristicProcessor: self.log.warn("Can't get wordcount") if self.totalwords < 50: - self.log.warn("flow is too short, not running heuristics") + self.log.warn('flow is too short, not running heuristics') return html is_abbyy = self.is_abbyy(html) @@ -754,7 +754,7 @@ class HeuristicProcessor: # <pre> tags), check and mark up line endings if required before proceeding # fix indents must run after this step if self.no_markup(html, 0.1): - self.log.debug("not enough paragraph markers, adding now") + self.log.debug('not enough paragraph markers, adding now') # markup using text processing html = self.markup_pre(html) @@ -768,8 +768,8 @@ class HeuristicProcessor: is_pdftohtml = self.is_pdftohtml(html) if is_pdftohtml: - self.line_open = "<(?P<outer>p)[^>]*>(\\s*<[ibu][^>]*>)?\\s*" - self.line_close = "\\s*(</[ibu][^>]*>\\s*)?</(?P=outer)>" + self.line_open = '<(?P<outer>p)[^>]*>(\\s*<[ibu][^>]*>)?\\s*' + self.line_close = '\\s*(</[ibu][^>]*>\\s*)?</(?P=outer)>' # ADE doesn't render <br />, change to empty paragraphs # html = re.sub('<br[^>]*>', u'<p>\u00a0</p>', html) @@ -789,7 +789,7 @@ class HeuristicProcessor: # If more than 40% of the lines are empty paragraphs and the user has enabled delete # blank paragraphs then delete blank lines to clean up spacing if self.blanks_between_paragraphs and getattr(self.extra_opts, 'delete_blank_paragraphs', False): - self.log.debug("deleting blank lines") + self.log.debug('deleting blank lines') self.blanks_deleted = True html = self.multi_blank.sub('\n<p class="softbreak" style="margin-top:.5em; page-break-before:avoid; text-align:center"> </p>', html) html = self.blankreg.sub('', html) @@ -804,18 +804,18 @@ class HeuristicProcessor: # more of the lines break in the same region of the document then unwrapping is required docanalysis = DocAnalysis(format, html) hardbreaks = docanalysis.line_histogram(.50) - self.log.debug("Hard line breaks check returned "+str(hardbreaks)) + self.log.debug('Hard line breaks check returned '+str(hardbreaks)) # Calculate Length unwrap_factor = getattr(self.extra_opts, 'html_unwrap_factor', 0.4) length = docanalysis.line_length(unwrap_factor) - self.log.debug("Median line length is " + str(length) + ", calculated with " + format + " format") + self.log.debug('Median line length is ' + str(length) + ', calculated with ' + format + ' format') # ##### Unwrap lines ###### if getattr(self.extra_opts, 'unwrap_lines', False): # only go through unwrapping code if the histogram shows unwrapping is required or if the user decreased the default unwrap_factor if hardbreaks or unwrap_factor < 0.4: - self.log.debug("Unwrapping required, unwrapping Lines") + self.log.debug('Unwrapping required, unwrapping Lines') # Dehyphenate with line length limiters dehyphenator = Dehyphenator(self.extra_opts.verbose, self.log) html = dehyphenator(html,'html', length) @@ -823,15 +823,15 @@ class HeuristicProcessor: if getattr(self.extra_opts, 'dehyphenate', False): # dehyphenate in cleanup mode to fix anything previous conversions/editing missed - self.log.debug("Fixing hyphenated content") + self.log.debug('Fixing hyphenated content') dehyphenator = Dehyphenator(self.extra_opts.verbose, self.log) html = dehyphenator(html,'html_cleanup', length) html = dehyphenator(html, 'individual_words', length) # If still no sections after unwrapping mark split points on lines with no punctuation if self.html_preprocess_sections < self.min_chapters and getattr(self.extra_opts, 'markup_chapter_headings', False): - self.log.debug("Looking for more split points based on punctuation," - " currently have " + str(self.html_preprocess_sections)) + self.log.debug('Looking for more split points based on punctuation,' + ' currently have ' + str(self.html_preprocess_sections)) chapdetect3 = re.compile( r'<(?P<styles>(p|div)[^>]*)>\s*(?P<section>(<span[^>]*>)?\s*(?!([\W]+\s*)+)' r'(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*' diff --git a/src/calibre/ebooks/covers.py b/src/calibre/ebooks/covers.py index 835cad8e26..934a6140ee 100644 --- a/src/calibre/ebooks/covers.py +++ b/src/calibre/ebooks/covers.py @@ -499,7 +499,7 @@ class Ornamental(Style): GUI_NAME = _('Ornamental') # SVG vectors {{{ - CORNER_VECTOR = "m 67.791903,64.260958 c -4.308097,-2.07925 -4.086719,-8.29575 0.334943,-9.40552 4.119758,-1.03399 8.732363,5.05239 5.393055,7.1162 -0.55,0.33992 -1,1.04147 -1,1.55902 0,1.59332 2.597425,1.04548 5.365141,-1.1316 1.999416,-1.57274 2.634859,-2.96609 2.634859,-5.7775 0,-9.55787 -9.827495,-13.42961 -24.43221,-9.62556 -3.218823,0.83839 -5.905663,1.40089 -5.970755,1.25 -0.06509,-0.1509 -0.887601,-1.19493 -1.827799,-2.32007 -1.672708,-2.00174 -1.636693,-2.03722 1.675668,-1.65052 1.861815,0.21736 6.685863,-0.35719 10.720107,-1.27678 12.280767,-2.79934 20.195487,-0.0248 22.846932,8.0092 3.187273,9.65753 -6.423297,17.7497 -15.739941,13.25313 z m 49.881417,-20.53932 c -3.19204,-2.701 -3.72967,-6.67376 -1.24009,-9.16334 2.48236,-2.48236 5.35141,-2.67905 7.51523,-0.51523 1.85966,1.85966 2.07045,6.52954 0.37143,8.22857 -2.04025,2.04024 3.28436,1.44595 6.92316,-0.77272 9.66959,-5.89579 0.88581,-18.22422 -13.0777,-18.35516 -5.28594,-0.0496 -10.31098,1.88721 -14.26764,5.4991 -1.98835,1.81509 -2.16454,1.82692 -2.7936,0.18763 -0.40973,-1.06774 0.12141,-2.82197 1.3628,-4.50104 2.46349,-3.33205 1.67564,-4.01299 -2.891784,-2.49938 -2.85998,0.94777 -3.81038,2.05378 -5.59837,6.51495 -1.184469,2.95536 -3.346819,6.86882 -4.805219,8.69657 -1.4584,1.82776 -2.65164,4.02223 -2.65164,4.87662 0,3.24694 -4.442667,0.59094 -5.872557,-3.51085 -1.361274,-3.90495 0.408198,-8.63869 4.404043,-11.78183 5.155844,-4.05558 1.612374,-3.42079 -9.235926,1.65457 -12.882907,6.02725 -16.864953,7.18038 -24.795556,7.18038 -8.471637,0 -13.38802,-1.64157 -17.634617,-5.88816 -2.832233,-2.83224 -3.849773,-4.81378 -4.418121,-8.6038 -1.946289,-12.9787795 8.03227,-20.91713135 19.767685,-15.7259993 5.547225,2.4538018 6.993631,6.1265383 3.999564,10.1557393 -5.468513,7.35914 -15.917883,-0.19431 -10.657807,-7.7041155 1.486298,-2.1219878 1.441784,-2.2225068 -0.984223,-2.2225068 -1.397511,0 -4.010527,1.3130878 -5.806704,2.9179718 -2.773359,2.4779995 -3.265777,3.5977995 -3.265777,7.4266705 0,5.10943 2.254112,8.84197 7.492986,12.40748 8.921325,6.07175 19.286666,5.61396 37.12088,-1.63946 15.35037,-6.24321 21.294999,-7.42408 34.886123,-6.92999 11.77046,0.4279 19.35803,3.05537 24.34054,8.42878 4.97758,5.3681 2.53939,13.58271 -4.86733,16.39873 -4.17361,1.58681 -11.00702,1.19681 -13.31978,-0.76018 z m 26.50156,-0.0787 c -2.26347,-2.50111 -2.07852,-7.36311 0.39995,-10.51398 2.68134,-3.40877 10.49035,-5.69409 18.87656,-5.52426 l 6.5685,0.13301 -7.84029,0.82767 c -8.47925,0.89511 -12.76997,2.82233 -16.03465,7.20213 -1.92294,2.57976 -1.96722,3.00481 -0.57298,5.5 1.00296,1.79495 2.50427,2.81821 4.46514,3.04333 2.92852,0.33623 2.93789,0.32121 1.08045,-1.73124 -1.53602,-1.69728 -1.64654,-2.34411 -0.61324,-3.58916 2.84565,-3.4288 7.14497,-0.49759 5.03976,3.43603 -1.86726,3.48903 -8.65528,4.21532 -11.3692,1.21647 z m -4.17462,-14.20302 c -0.38836,-0.62838 -0.23556,-1.61305 0.33954,-2.18816 1.3439,-1.34389 4.47714,-0.17168 3.93038,1.47045 -0.5566,1.67168 -3.38637,2.14732 -4.26992,0.71771 z m -8.48037,-9.1829 c -12.462,-4.1101 -12.53952,-4.12156 -25.49998,-3.7694 -24.020921,0.65269 -32.338219,0.31756 -37.082166,-1.49417 -5.113999,-1.95305 -8.192504,-6.3647405 -6.485463,-9.2940713 0.566827,-0.972691 1.020091,-1.181447 1.037211,-0.477701 0.01685,0.692606 1.268676,1.2499998 2.807321,1.2499998 1.685814,0 4.868609,1.571672 8.10041,4.0000015 4.221481,3.171961 6.182506,3.999221 9.473089,3.996261 l 4.149585,-0.004 -3.249996,-1.98156 c -3.056252,-1.863441 -4.051566,-3.8760635 -2.623216,-5.3044145 0.794,-0.794 6.188222,1.901516 9.064482,4.5295635 1.858669,1.698271 3.461409,1.980521 10.559493,1.859621 11.30984,-0.19266 20.89052,1.29095 31.97905,4.95208 7.63881,2.52213 11.51931,3.16471 22.05074,3.65141 7.02931,0.32486 13.01836,0.97543 13.30902,1.44571 0.29065,0.47029 -5.2356,0.83436 -12.28056,0.80906 -12.25942,-0.044 -13.34537,-0.2229 -25.30902,-4.16865 z" # noqa: E501 + CORNER_VECTOR = 'm 67.791903,64.260958 c -4.308097,-2.07925 -4.086719,-8.29575 0.334943,-9.40552 4.119758,-1.03399 8.732363,5.05239 5.393055,7.1162 -0.55,0.33992 -1,1.04147 -1,1.55902 0,1.59332 2.597425,1.04548 5.365141,-1.1316 1.999416,-1.57274 2.634859,-2.96609 2.634859,-5.7775 0,-9.55787 -9.827495,-13.42961 -24.43221,-9.62556 -3.218823,0.83839 -5.905663,1.40089 -5.970755,1.25 -0.06509,-0.1509 -0.887601,-1.19493 -1.827799,-2.32007 -1.672708,-2.00174 -1.636693,-2.03722 1.675668,-1.65052 1.861815,0.21736 6.685863,-0.35719 10.720107,-1.27678 12.280767,-2.79934 20.195487,-0.0248 22.846932,8.0092 3.187273,9.65753 -6.423297,17.7497 -15.739941,13.25313 z m 49.881417,-20.53932 c -3.19204,-2.701 -3.72967,-6.67376 -1.24009,-9.16334 2.48236,-2.48236 5.35141,-2.67905 7.51523,-0.51523 1.85966,1.85966 2.07045,6.52954 0.37143,8.22857 -2.04025,2.04024 3.28436,1.44595 6.92316,-0.77272 9.66959,-5.89579 0.88581,-18.22422 -13.0777,-18.35516 -5.28594,-0.0496 -10.31098,1.88721 -14.26764,5.4991 -1.98835,1.81509 -2.16454,1.82692 -2.7936,0.18763 -0.40973,-1.06774 0.12141,-2.82197 1.3628,-4.50104 2.46349,-3.33205 1.67564,-4.01299 -2.891784,-2.49938 -2.85998,0.94777 -3.81038,2.05378 -5.59837,6.51495 -1.184469,2.95536 -3.346819,6.86882 -4.805219,8.69657 -1.4584,1.82776 -2.65164,4.02223 -2.65164,4.87662 0,3.24694 -4.442667,0.59094 -5.872557,-3.51085 -1.361274,-3.90495 0.408198,-8.63869 4.404043,-11.78183 5.155844,-4.05558 1.612374,-3.42079 -9.235926,1.65457 -12.882907,6.02725 -16.864953,7.18038 -24.795556,7.18038 -8.471637,0 -13.38802,-1.64157 -17.634617,-5.88816 -2.832233,-2.83224 -3.849773,-4.81378 -4.418121,-8.6038 -1.946289,-12.9787795 8.03227,-20.91713135 19.767685,-15.7259993 5.547225,2.4538018 6.993631,6.1265383 3.999564,10.1557393 -5.468513,7.35914 -15.917883,-0.19431 -10.657807,-7.7041155 1.486298,-2.1219878 1.441784,-2.2225068 -0.984223,-2.2225068 -1.397511,0 -4.010527,1.3130878 -5.806704,2.9179718 -2.773359,2.4779995 -3.265777,3.5977995 -3.265777,7.4266705 0,5.10943 2.254112,8.84197 7.492986,12.40748 8.921325,6.07175 19.286666,5.61396 37.12088,-1.63946 15.35037,-6.24321 21.294999,-7.42408 34.886123,-6.92999 11.77046,0.4279 19.35803,3.05537 24.34054,8.42878 4.97758,5.3681 2.53939,13.58271 -4.86733,16.39873 -4.17361,1.58681 -11.00702,1.19681 -13.31978,-0.76018 z m 26.50156,-0.0787 c -2.26347,-2.50111 -2.07852,-7.36311 0.39995,-10.51398 2.68134,-3.40877 10.49035,-5.69409 18.87656,-5.52426 l 6.5685,0.13301 -7.84029,0.82767 c -8.47925,0.89511 -12.76997,2.82233 -16.03465,7.20213 -1.92294,2.57976 -1.96722,3.00481 -0.57298,5.5 1.00296,1.79495 2.50427,2.81821 4.46514,3.04333 2.92852,0.33623 2.93789,0.32121 1.08045,-1.73124 -1.53602,-1.69728 -1.64654,-2.34411 -0.61324,-3.58916 2.84565,-3.4288 7.14497,-0.49759 5.03976,3.43603 -1.86726,3.48903 -8.65528,4.21532 -11.3692,1.21647 z m -4.17462,-14.20302 c -0.38836,-0.62838 -0.23556,-1.61305 0.33954,-2.18816 1.3439,-1.34389 4.47714,-0.17168 3.93038,1.47045 -0.5566,1.67168 -3.38637,2.14732 -4.26992,0.71771 z m -8.48037,-9.1829 c -12.462,-4.1101 -12.53952,-4.12156 -25.49998,-3.7694 -24.020921,0.65269 -32.338219,0.31756 -37.082166,-1.49417 -5.113999,-1.95305 -8.192504,-6.3647405 -6.485463,-9.2940713 0.566827,-0.972691 1.020091,-1.181447 1.037211,-0.477701 0.01685,0.692606 1.268676,1.2499998 2.807321,1.2499998 1.685814,0 4.868609,1.571672 8.10041,4.0000015 4.221481,3.171961 6.182506,3.999221 9.473089,3.996261 l 4.149585,-0.004 -3.249996,-1.98156 c -3.056252,-1.863441 -4.051566,-3.8760635 -2.623216,-5.3044145 0.794,-0.794 6.188222,1.901516 9.064482,4.5295635 1.858669,1.698271 3.461409,1.980521 10.559493,1.859621 11.30984,-0.19266 20.89052,1.29095 31.97905,4.95208 7.63881,2.52213 11.51931,3.16471 22.05074,3.65141 7.02931,0.32486 13.01836,0.97543 13.30902,1.44571 0.29065,0.47029 -5.2356,0.83436 -12.28056,0.80906 -12.25942,-0.044 -13.34537,-0.2229 -25.30902,-4.16865 z' # noqa: E501 # }}} PATH_CACHE = {} VIEWPORT = (400, 500) diff --git a/src/calibre/ebooks/djvu/djvubzzdec.py b/src/calibre/ebooks/djvu/djvubzzdec.py index 5748af9682..94fd53bac6 100644 --- a/src/calibre/ebooks/djvu/djvubzzdec.py +++ b/src/calibre/ebooks/djvu/djvubzzdec.py @@ -79,14 +79,14 @@ MAXLEN = 1024 ** 2 class BZZDecoderError(Exception): - """This exception is raised when BZZDecode runs into trouble - """ + '''This exception is raised when BZZDecode runs into trouble + ''' def __init__(self, msg): self.msg = msg def __str__(self): - return "BZZDecoderError: %s" % (self.msg) + return 'BZZDecoderError: %s' % (self.msg) # This table has been designed for the ZPCoder @@ -468,7 +468,7 @@ class BZZDecoder(): self.byte = 0xff self.delay -= 1 if self.delay < 1: - raise BZZDecoderError("BiteStream EOF") + raise BZZDecoderError('BiteStream EOF') self.bufint = (self.bufint << 8) | self.byte self.scount += 8 @@ -486,7 +486,7 @@ class BZZDecoder(): if not self.xsize: return 0 if self.xsize > MAXBLOCK * 1024: # 4MB (4096 * 1024) is max block - raise BZZDecoderError("BiteStream.corrupt") + raise BZZDecoderError('BiteStream.corrupt') # Dec11ode Estimation Speed fshift = 0 if self.zpcodec_decoder(): @@ -571,7 +571,7 @@ class BZZDecoder(): # //////// Reconstruct the string if markerpos < 1 or markerpos >= self.xsize: - raise BZZDecoderError("BiteStream.corrupt") + raise BZZDecoderError('BiteStream.corrupt') # Allocate pointers posn = [0] * self.xsize # Prepare count buffer @@ -602,7 +602,7 @@ class BZZDecoder(): i = count[c] + (n & 0xffffff) # Free and check if i != markerpos: - raise BZZDecoderError("BiteStream.corrupt") + raise BZZDecoderError('BiteStream.corrupt') return self.xsize def decode_raw(self, bits): @@ -733,10 +733,10 @@ def main(): import sys from calibre_extensions import bzzdec as d - with open(sys.argv[1], "rb") as f: + with open(sys.argv[1], 'rb') as f: raw = f.read() print(d.decompress(raw)) -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/src/calibre/ebooks/docx/index.py b/src/calibre/ebooks/docx/index.py index a00b158392..16800f3197 100644 --- a/src/calibre/ebooks/docx/index.py +++ b/src/calibre/ebooks/docx/index.py @@ -143,7 +143,7 @@ def split_up_block(block, a, text, parts, ldict): ldict[span] = len(prefix) -""" +''' The merge algorithm is a little tricky. We start with a list of elementary blocks. Each is an HtmlElement, a p node with a list of child nodes. The last child may be a link, and the earlier ones are @@ -175,7 +175,7 @@ If we find such a matching entry, go back to the start with (p ... pk+1) and (n If there is no matching entry, then because of the original reversed order we want to insert nk+1 and all following entries from n into p immediately following pk. -""" +''' def find_match(prev_block, pind, nextent, ldict): @@ -208,7 +208,7 @@ def add_link(pent, nent, ldict): p.insert(p.index(pa) + 1, na) else: # substitute link na for plain text in pent - pent.text = "" + pent.text = '' pent.append(na) diff --git a/src/calibre/ebooks/docx/to_html.py b/src/calibre/ebooks/docx/to_html.py index 54b9578573..4560006c30 100644 --- a/src/calibre/ebooks/docx/to_html.py +++ b/src/calibre/ebooks/docx/to_html.py @@ -683,7 +683,7 @@ class Convert: multi_spaces = self.ms_pat.search(ctext) is not None preserve = multi_spaces or self.ws_pat.search(ctext) is not None if preserve: - text.add_elem(SPAN(ctext, style="white-space:pre-wrap")) + text.add_elem(SPAN(ctext, style='white-space:pre-wrap')) ans.append(text.elem) else: text.buf.append(ctext) diff --git a/src/calibre/ebooks/docx/writer/container.py b/src/calibre/ebooks/docx/writer/container.py index 0b8bff1917..03f2ea90e7 100644 --- a/src/calibre/ebooks/docx/writer/container.py +++ b/src/calibre/ebooks/docx/writer/container.py @@ -70,7 +70,7 @@ def create_skeleton(opts, namespaces=None): E.pgSz(**{w('w'):str(width), w('h'):str(height)}), E.pgMar(**dict(map(margin, 'left top right bottom'.split()))), E.cols(**{w('space'):'720'}), - E.docGrid(**{w('linePitch'):"360"}), + E.docGrid(**{w('linePitch'):'360'}), )) dn = {k:v for k, v in iteritems(namespaces) if k in tuple('wra') + ('wp',)} @@ -79,15 +79,15 @@ def create_skeleton(opts, namespaces=None): E.docDefaults( E.rPrDefault( E.rPr( - E.rFonts(**{w('asciiTheme'):"minorHAnsi", w('eastAsiaTheme'):"minorEastAsia", w('hAnsiTheme'):"minorHAnsi", w('cstheme'):"minorBidi"}), + E.rFonts(**{w('asciiTheme'):'minorHAnsi', w('eastAsiaTheme'):'minorEastAsia', w('hAnsiTheme'):'minorHAnsi', w('cstheme'):'minorBidi'}), E.sz(**{w('val'):'22'}), E.szCs(**{w('val'):'22'}), - E.lang(**{w('val'):'en-US', w('eastAsia'):"en-US", w('bidi'):"ar-SA"}) + E.lang(**{w('val'):'en-US', w('eastAsia'):'en-US', w('bidi'):'ar-SA'}) ) ), E.pPrDefault( E.pPr( - E.spacing(**{w('after'):"0", w('line'):"276", w('lineRule'):"auto"}) + E.spacing(**{w('after'):'0', w('line'):'276', w('lineRule'):'auto'}) ) ) ) @@ -173,25 +173,25 @@ class DOCX: E = ElementMaker(namespace=self.namespace.namespaces['ct'], nsmap={None:self.namespace.namespaces['ct']}) types = E.Types() for partname, mt in iteritems({ - "/word/footnotes.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml", - "/word/document.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml", - "/word/numbering.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml", - "/word/styles.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", - "/word/endnotes.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml", - "/word/settings.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml", - "/word/theme/theme1.xml": "application/vnd.openxmlformats-officedocument.theme+xml", - "/word/fontTable.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml", - "/word/webSettings.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml", - "/docProps/core.xml": "application/vnd.openxmlformats-package.core-properties+xml", - "/docProps/app.xml": "application/vnd.openxmlformats-officedocument.extended-properties+xml", + '/word/footnotes.xml': 'application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml', + '/word/document.xml': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml', + '/word/numbering.xml': 'application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml', + '/word/styles.xml': 'application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml', + '/word/endnotes.xml': 'application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml', + '/word/settings.xml': 'application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml', + '/word/theme/theme1.xml': 'application/vnd.openxmlformats-officedocument.theme+xml', + '/word/fontTable.xml': 'application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml', + '/word/webSettings.xml': 'application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml', + '/docProps/core.xml': 'application/vnd.openxmlformats-package.core-properties+xml', + '/docProps/app.xml': 'application/vnd.openxmlformats-officedocument.extended-properties+xml', }): types.append(E.Override(PartName=partname, ContentType=mt)) added = {'png', 'gif', 'jpeg', 'jpg', 'svg', 'xml'} for ext in added: types.append(E.Default(Extension=ext, ContentType=guess_type('a.'+ext)[0])) for ext, mt in iteritems({ - "rels": "application/vnd.openxmlformats-package.relationships+xml", - "odttf": "application/vnd.openxmlformats-officedocument.obfuscatedFont", + 'rels': 'application/vnd.openxmlformats-package.relationships+xml', + 'odttf': 'application/vnd.openxmlformats-officedocument.obfuscatedFont', }): added.add(ext) types.append(E.Default(Extension=ext, ContentType=mt)) @@ -242,7 +242,7 @@ class DOCX: def convert_metadata(self, mi): namespaces = self.namespace.namespaces E = ElementMaker(namespace=namespaces['cp'], nsmap={x:namespaces[x] for x in 'cp dc dcterms xsi'.split()}) - cp = E.coreProperties(E.revision("1"), E.lastModifiedBy('calibre')) + cp = E.coreProperties(E.revision('1'), E.lastModifiedBy('calibre')) ts = utcnow().isoformat(native_string_type('T')).rpartition('.')[0] + 'Z' for x in 'created modified'.split(): x = cp.makeelement('{{{}}}{}'.format(namespaces['dcterms'], x), **{'{%s}type' % namespaces['xsi']:'dcterms:W3CDTF'}) diff --git a/src/calibre/ebooks/docx/writer/fonts.py b/src/calibre/ebooks/docx/writer/fonts.py index de02ad4eec..22e3989f2c 100644 --- a/src/calibre/ebooks/docx/writer/fonts.py +++ b/src/calibre/ebooks/docx/writer/fonts.py @@ -73,4 +73,4 @@ class FontsManager: font_data_map['word/' + fname] = obfuscate_font_data(item.data, key) makeelement(font, 'w:embed' + tag, r_id=rid, w_fontKey='{%s}' % key.urn.rpartition(':')[-1].upper(), - w_subsetted="true" if self.opts.subset_embedded_fonts else "false") + w_subsetted='true' if self.opts.subset_embedded_fonts else 'false') diff --git a/src/calibre/ebooks/docx/writer/images.py b/src/calibre/ebooks/docx/writer/images.py index 5e4d6d946d..214c014efc 100644 --- a/src/calibre/ebooks/docx/writer/images.py +++ b/src/calibre/ebooks/docx/writer/images.py @@ -145,8 +145,8 @@ class ImagesManager: parent = makeelement(ans, 'wp:anchor', **get_image_margins(style)) # The next three lines are boilerplate that Word requires, even # though the DOCX specs define defaults for all of them - parent.set('simplePos', '0'), parent.set('relativeHeight', '1'), parent.set('behindDoc',"0"), parent.set('locked', "0") - parent.set('layoutInCell', "1"), parent.set('allowOverlap', '1') + parent.set('simplePos', '0'), parent.set('relativeHeight', '1'), parent.set('behindDoc','0'), parent.set('locked', '0') + parent.set('layoutInCell', '1'), parent.set('allowOverlap', '1') makeelement(parent, 'wp:simplePos', x='0', y='0') makeelement(makeelement(parent, 'wp:positionH', relativeFrom='margin'), 'wp:align').text = floating makeelement(makeelement(parent, 'wp:positionV', relativeFrom='line'), 'wp:align').text = 'top' @@ -169,7 +169,7 @@ class ImagesManager: def create_docx_image_markup(self, parent, name, alt, img_rid, width, height, svg_rid=''): makeelement, namespaces = self.document_relationships.namespace.makeelement, self.document_relationships.namespace.namespaces makeelement(parent, 'wp:docPr', id=str(self.count), name=name, descr=alt) - makeelement(makeelement(parent, 'wp:cNvGraphicFramePr'), 'a:graphicFrameLocks', noChangeAspect="1") + makeelement(makeelement(parent, 'wp:cNvGraphicFramePr'), 'a:graphicFrameLocks', noChangeAspect='1') g = makeelement(parent, 'a:graphic') gd = makeelement(g, 'a:graphicData', uri=namespaces['pic']) pic = makeelement(gd, 'pic:pic') @@ -231,8 +231,8 @@ class ImagesManager: root = etree.Element('root', nsmap=namespaces) ans = makeelement(root, 'w:drawing', append=False) parent = makeelement(ans, 'wp:anchor', **{'dist'+edge:'0' for edge in 'LRTB'}) - parent.set('simplePos', '0'), parent.set('relativeHeight', '1'), parent.set('behindDoc',"0"), parent.set('locked', "0") - parent.set('layoutInCell', "1"), parent.set('allowOverlap', '1') + parent.set('simplePos', '0'), parent.set('relativeHeight', '1'), parent.set('behindDoc','0'), parent.set('locked', '0') + parent.set('layoutInCell', '1'), parent.set('allowOverlap', '1') makeelement(parent, 'wp:simplePos', x='0', y='0') makeelement(makeelement(parent, 'wp:positionH', relativeFrom='page'), 'wp:align').text = 'center' makeelement(makeelement(parent, 'wp:positionV', relativeFrom='page'), 'wp:align').text = 'center' diff --git a/src/calibre/ebooks/docx/writer/links.py b/src/calibre/ebooks/docx/writer/links.py index 647b06298e..b34c2c215f 100644 --- a/src/calibre/ebooks/docx/writer/links.py +++ b/src/calibre/ebooks/docx/writer/links.py @@ -36,7 +36,7 @@ class TOCItem: def serialize(self, body, makeelement): p = makeelement(body, 'w:p', append=False) ppr = makeelement(p, 'w:pPr') - makeelement(ppr, 'w:pStyle', w_val="Normal") + makeelement(ppr, 'w:pStyle', w_val='Normal') makeelement(ppr, 'w:ind', w_left='0', w_firstLineChars='0', w_firstLine='0', w_leftChars=str(200 * self.level)) if self.is_first: makeelement(ppr, 'w:pageBreakBefore', w_val='off') diff --git a/src/calibre/ebooks/docx/writer/lists.py b/src/calibre/ebooks/docx/writer/lists.py index 0859530d55..aced84081b 100644 --- a/src/calibre/ebooks/docx/writer/lists.py +++ b/src/calibre/ebooks/docx/writer/lists.py @@ -121,7 +121,7 @@ class Level: makeelement(makeelement(lvl, 'w:pPr'), 'w:ind', w_hanging='360', w_left=str(1152 + self.ilvl * 360)) if self.num_fmt == 'bullet': ff = {'\uf0b7':'Symbol', '\uf0a7':'Wingdings'}.get(self.lvl_text, 'Courier New') - makeelement(makeelement(lvl, 'w:rPr'), 'w:rFonts', w_ascii=ff, w_hAnsi=ff, w_hint="default") + makeelement(makeelement(lvl, 'w:rPr'), 'w:rFonts', w_ascii=ff, w_hAnsi=ff, w_hint='default') class ListsManager: diff --git a/src/calibre/ebooks/docx/writer/tables.py b/src/calibre/ebooks/docx/writer/tables.py index 3f65d6a381..7f2e83b28c 100644 --- a/src/calibre/ebooks/docx/writer/tables.py +++ b/src/calibre/ebooks/docx/writer/tables.py @@ -121,7 +121,7 @@ class Cell: # cell level bc = self.background_color or self.row.background_color or self.row.table.background_color if bc: - makeelement(tcPr, 'w:shd', w_val="clear", w_color="auto", w_fill=bc) + makeelement(tcPr, 'w:shd', w_val='clear', w_color='auto', w_fill=bc) b = makeelement(tcPr, 'w:tcBorders', append=False) for edge, border in iteritems(self.borders): diff --git a/src/calibre/ebooks/epub/pages.py b/src/calibre/ebooks/epub/pages.py index 7f58aa728c..bfccca2090 100644 --- a/src/calibre/ebooks/epub/pages.py +++ b/src/calibre/ebooks/epub/pages.py @@ -48,7 +48,7 @@ def add_page_map(opfpath, opts): oeb = OEBBook(opfpath) selector = XPath(opts.page, namespaces=NSMAP) name_for = build_name_for(opts.page_names) - idgen = ("calibre-page-%d" % n for n in count(1)) + idgen = ('calibre-page-%d' % n for n in count(1)) for item in oeb.spine: data = item.data for elem in selector(data): diff --git a/src/calibre/ebooks/html_transform_rules.py b/src/calibre/ebooks/html_transform_rules.py index f5e2634c88..7bb85adacb 100644 --- a/src/calibre/ebooks/html_transform_rules.py +++ b/src/calibre/ebooks/html_transform_rules.py @@ -380,7 +380,7 @@ class Rule: self.css_selector = '.' + q self.selector = self.css elif mt == 'not_has_class': - self.css_selector = f":not(.{q})" + self.css_selector = f':not(.{q})' self.selector = self.css elif mt == 'contains_text': self.xpath_selector = XPath(f'//*[contains(text(), {text_as_xpath_literal(q)})]') @@ -627,7 +627,7 @@ def test(return_tests=False): # {{{ self.assertTrue(t('add_attrs', "class='c' data-m=n")(p)) self.ae(p.items(), [('class', 'c'), ('data-m', 'n')]) p = r('<p a=1>')[0] - self.assertTrue(t('add_attrs', "a=2")(p)) + self.assertTrue(t('add_attrs', 'a=2')(p)) self.ae(p.items(), [('a', '2')]) p = r('<p>t<span>s')[0] diff --git a/src/calibre/ebooks/hyphenate.py b/src/calibre/ebooks/hyphenate.py index 78480cdf57..5fc27b9ee3 100644 --- a/src/calibre/ebooks/hyphenate.py +++ b/src/calibre/ebooks/hyphenate.py @@ -29,13 +29,13 @@ class Hyphenator: self.exceptions = {} for ex in exceptions.split(): # Convert the hyphenated pattern into a point array for use later. - self.exceptions[ex.replace('-', '')] = [0] + [int(h == '-') for h in re.split(r"[a-z]", ex)] + self.exceptions[ex.replace('-', '')] = [0] + [int(h == '-') for h in re.split(r'[a-z]', ex)] def _insert_pattern(self, pattern): # Convert a pattern like 'a1bc3d4' into a string of chars 'abcd' # and a list of points [ 1, 0, 3, 4 ]. chars = re.sub('[0-9]', '', pattern) - points = [int(d or 0) for d in re.split("[.a-z]", pattern)] + points = [int(d or 0) for d in re.split('[.a-z]', pattern)] # Insert the pattern into the tree. Each character finds a dict # another level down in the tree, and leaf nodes have the list of @@ -48,9 +48,9 @@ class Hyphenator: t[None] = points def hyphenate_word(self, word): - """ Given a word, returns a list of pieces, broken at the possible + ''' Given a word, returns a list of pieces, broken at the possible hyphenation points. - """ + ''' # Short words aren't hyphenated. if len(word) <= 4: return [word] @@ -86,7 +86,7 @@ class Hyphenator: patterns = ( # Knuth and Liang's original hyphenation patterns from classic TeX. # In the public domain. -""" +''' .ach4 .ad4der .af1t .al3t .am5at .an5c .ang4 .ani5m .ant4 .an3te .anti5s .ar5s .ar4tie .ar4ty .as3c .as1p .as1s .aster5 .atom5 .au1d .av4i .awn4 .ba4g .ba5na .bas4e .ber4 .be5ra .be3sm .be5sto .bri2 .but4ti .cam4pe .can5c .capa5b .car5ol @@ -436,7 +436,7 @@ ympa3 yn3chr yn5d yn5g yn5ic 5ynx y1o4 yo5d y4o5g yom4 yo5net y4ons y4os y4ped yper5 yp3i y3po y4poc yp2ta y5pu yra5m yr5ia y3ro yr4r ys4c y3s2e ys3ica ys3io 3ysis y4so yss4 ys1t ys3ta ysur4 y3thin yt3ic y1w za1 z5a2b zar2 4zb 2ze ze4n ze4p z1er ze3ro zet4 2z1i z4il z4is 5zl 4zm 1zo zo4m zo5ol zte4 4z1z2 z4zy -""" +''' # Extra patterns, from ushyphmax.tex, dated 2005-05-30. # Copyright (C) 1990, 2004, 2005 Gerard D.C. Kuiken. # Copying and distribution of this file, with or without modification, @@ -446,7 +446,7 @@ ze4p z1er ze3ro zet4 2z1i z4il z4is 5zl 4zm 1zo zo4m zo5ol zte4 4z1z2 z4zy # These patterns are based on the Hyphenation Exception Log # published in TUGboat, Volume 10 (1989), No. 3, pp. 337-341, # and a large number of incorrectly hyphenated words not yet published. -""" +''' .con5gr .de5riva .dri5v4 .eth1y6l1 .eu4ler .ev2 .ever5si5b .ga4s1om1 .ge4ome .ge5ot1 .he3mo1 .he3p6a .he3roe .in5u2t .kil2n3i .ko6r1te1 .le6ices .me4ga1l .met4ala .mim5i2c1 .mi1s4ers .ne6o3f .noe1th .non1e2m .poly1s .post1am .pre1am @@ -502,13 +502,13 @@ uea1m u2r1al. uri4al. us2er. v1ativ v1oir5du1 va6guer vaude3v 1verely. v1er1eig ves1tite vi1vip3a3r voice1p waste3w6a2 wave1g4 w3c week1n wide5sp wo4k1en wrap3aro writ6er. x1q xquis3 y5che3d ym5e5try y1stro yes5ter1y z3ian. z3o1phr z2z3w -""") +''') -exceptions = """ +exceptions = ''' as-so-ciate as-so-ciates dec-li-na-tion oblig-a-tory phil-an-thropic present presents project projects reci-procity re-cog-ni-zance ref-or-ma-tion ret-ri-bu-tion ta-ble -""" +''' hyphenator = Hyphenator(patterns, exceptions) hyphenate_word = hyphenator.hyphenate_word diff --git a/src/calibre/ebooks/lit/maps/__init__.py b/src/calibre/ebooks/lit/maps/__init__.py index 13114268d9..9b381662e3 100644 --- a/src/calibre/ebooks/lit/maps/__init__.py +++ b/src/calibre/ebooks/lit/maps/__init__.py @@ -1,9 +1,9 @@ __license__ = 'GPL v3' __copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>' -""" +''' Microsoft LIT tag and attribute tables. -""" +''' from calibre.ebooks.lit.maps.html import MAP as HTML_MAP from calibre.ebooks.lit.maps.opf import MAP as OPF_MAP diff --git a/src/calibre/ebooks/lit/maps/html.py b/src/calibre/ebooks/lit/maps/html.py index c144d55ea8..92c249d25c 100644 --- a/src/calibre/ebooks/lit/maps/html.py +++ b/src/calibre/ebooks/lit/maps/html.py @@ -1,903 +1,903 @@ __license__ = 'GPL v3' __copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>' -""" +''' Microsoft LIT HTML tag and attribute tables, copied from ConvertLIT. -""" +''' TAGS = [ None, None, None, - "a", - "acronym", - "address", - "applet", - "area", - "b", - "base", - "basefont", - "bdo", - "bgsound", - "big", - "blink", - "blockquote", - "body", - "br", - "button", - "caption", - "center", - "cite", - "code", - "col", - "colgroup", + 'a', + 'acronym', + 'address', + 'applet', + 'area', + 'b', + 'base', + 'basefont', + 'bdo', + 'bgsound', + 'big', + 'blink', + 'blockquote', + 'body', + 'br', + 'button', + 'caption', + 'center', + 'cite', + 'code', + 'col', + 'colgroup', None, None, - "dd", - "del", - "dfn", - "dir", - "div", - "dl", - "dt", - "em", - "embed", - "fieldset", - "font", - "form", - "frame", - "frameset", + 'dd', + 'del', + 'dfn', + 'dir', + 'div', + 'dl', + 'dt', + 'em', + 'embed', + 'fieldset', + 'font', + 'form', + 'frame', + 'frameset', None, - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "head", - "hr", - "html", - "i", - "iframe", - "img", - "input", - "ins", - "kbd", - "label", - "legend", - "li", - "link", - "tag61", - "map", - "tag63", - "tag64", - "meta", - "nextid", - "nobr", - "noembed", - "noframes", - "noscript", - "object", - "ol", - "option", - "p", - "param", - "plaintext", - "pre", - "q", - "rp", - "rt", - "ruby", - "s", - "samp", - "script", - "select", - "small", - "span", - "strike", - "strong", - "style", - "sub", - "sup", - "table", - "tbody", - "tc", - "td", - "textarea", - "tfoot", - "th", - "thead", - "title", - "tr", - "tt", - "u", - "ul", - "var", - "wbr", + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'head', + 'hr', + 'html', + 'i', + 'iframe', + 'img', + 'input', + 'ins', + 'kbd', + 'label', + 'legend', + 'li', + 'link', + 'tag61', + 'map', + 'tag63', + 'tag64', + 'meta', + 'nextid', + 'nobr', + 'noembed', + 'noframes', + 'noscript', + 'object', + 'ol', + 'option', + 'p', + 'param', + 'plaintext', + 'pre', + 'q', + 'rp', + 'rt', + 'ruby', + 's', + 'samp', + 'script', + 'select', + 'small', + 'span', + 'strike', + 'strong', + 'style', + 'sub', + 'sup', + 'table', + 'tbody', + 'tc', + 'td', + 'textarea', + 'tfoot', + 'th', + 'thead', + 'title', + 'tr', + 'tt', + 'u', + 'ul', + 'var', + 'wbr', None, ] ATTRS0 = { - 0x8010: "tabindex", - 0x8046: "title", - 0x804b: "style", - 0x804d: "disabled", - 0x83ea: "class", - 0x83eb: "id", - 0x83fe: "datafld", - 0x83ff: "datasrc", - 0x8400: "dataformatas", - 0x87d6: "accesskey", - 0x9392: "lang", - 0x93ed: "language", - 0x93fe: "dir", - 0x9771: "onmouseover", - 0x9772: "onmouseout", - 0x9773: "onmousedown", - 0x9774: "onmouseup", - 0x9775: "onmousemove", - 0x9776: "onkeydown", - 0x9777: "onkeyup", - 0x9778: "onkeypress", - 0x9779: "onclick", - 0x977a: "ondblclick", - 0x977e: "onhelp", - 0x977f: "onfocus", - 0x9780: "onblur", - 0x9783: "onrowexit", - 0x9784: "onrowenter", - 0x9786: "onbeforeupdate", - 0x9787: "onafterupdate", - 0x978a: "onreadystatechange", - 0x9790: "onscroll", - 0x9794: "ondragstart", - 0x9795: "onresize", - 0x9796: "onselectstart", - 0x9797: "onerrorupdate", - 0x9799: "ondatasetchanged", - 0x979a: "ondataavailable", - 0x979b: "ondatasetcomplete", - 0x979c: "onfilterchange", - 0x979f: "onlosecapture", - 0x97a0: "onpropertychange", - 0x97a2: "ondrag", - 0x97a3: "ondragend", - 0x97a4: "ondragenter", - 0x97a5: "ondragover", - 0x97a6: "ondragleave", - 0x97a7: "ondrop", - 0x97a8: "oncut", - 0x97a9: "oncopy", - 0x97aa: "onpaste", - 0x97ab: "onbeforecut", - 0x97ac: "onbeforecopy", - 0x97ad: "onbeforepaste", - 0x97af: "onrowsdelete", - 0x97b0: "onrowsinserted", - 0x97b1: "oncellchange", - 0x97b2: "oncontextmenu", - 0x97b6: "onbeforeeditfocus", + 0x8010: 'tabindex', + 0x8046: 'title', + 0x804b: 'style', + 0x804d: 'disabled', + 0x83ea: 'class', + 0x83eb: 'id', + 0x83fe: 'datafld', + 0x83ff: 'datasrc', + 0x8400: 'dataformatas', + 0x87d6: 'accesskey', + 0x9392: 'lang', + 0x93ed: 'language', + 0x93fe: 'dir', + 0x9771: 'onmouseover', + 0x9772: 'onmouseout', + 0x9773: 'onmousedown', + 0x9774: 'onmouseup', + 0x9775: 'onmousemove', + 0x9776: 'onkeydown', + 0x9777: 'onkeyup', + 0x9778: 'onkeypress', + 0x9779: 'onclick', + 0x977a: 'ondblclick', + 0x977e: 'onhelp', + 0x977f: 'onfocus', + 0x9780: 'onblur', + 0x9783: 'onrowexit', + 0x9784: 'onrowenter', + 0x9786: 'onbeforeupdate', + 0x9787: 'onafterupdate', + 0x978a: 'onreadystatechange', + 0x9790: 'onscroll', + 0x9794: 'ondragstart', + 0x9795: 'onresize', + 0x9796: 'onselectstart', + 0x9797: 'onerrorupdate', + 0x9799: 'ondatasetchanged', + 0x979a: 'ondataavailable', + 0x979b: 'ondatasetcomplete', + 0x979c: 'onfilterchange', + 0x979f: 'onlosecapture', + 0x97a0: 'onpropertychange', + 0x97a2: 'ondrag', + 0x97a3: 'ondragend', + 0x97a4: 'ondragenter', + 0x97a5: 'ondragover', + 0x97a6: 'ondragleave', + 0x97a7: 'ondrop', + 0x97a8: 'oncut', + 0x97a9: 'oncopy', + 0x97aa: 'onpaste', + 0x97ab: 'onbeforecut', + 0x97ac: 'onbeforecopy', + 0x97ad: 'onbeforepaste', + 0x97af: 'onrowsdelete', + 0x97b0: 'onrowsinserted', + 0x97b1: 'oncellchange', + 0x97b2: 'oncontextmenu', + 0x97b6: 'onbeforeeditfocus', } ATTRS3 = { - 0x0001: "href", - 0x03ec: "target", - 0x03ee: "rel", - 0x03ef: "rev", - 0x03f0: "urn", - 0x03f1: "methods", - 0x8001: "name", - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x0001: 'href', + 0x03ec: 'target', + 0x03ee: 'rel', + 0x03ef: 'rev', + 0x03f0: 'urn', + 0x03f1: 'methods', + 0x8001: 'name', + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS5 = { - 0x9399: "clear", + 0x9399: 'clear', } ATTRS6 = { - 0x8001: "name", - 0x8006: "width", - 0x8007: "height", - 0x804a: "align", - 0x8bbb: "classid", - 0x8bbc: "data", - 0x8bbf: "codebase", - 0x8bc0: "codetype", - 0x8bc1: "code", - 0x8bc2: "type", - 0x8bc5: "vspace", - 0x8bc6: "hspace", - 0x978e: "onerror", + 0x8001: 'name', + 0x8006: 'width', + 0x8007: 'height', + 0x804a: 'align', + 0x8bbb: 'classid', + 0x8bbc: 'data', + 0x8bbf: 'codebase', + 0x8bc0: 'codetype', + 0x8bc1: 'code', + 0x8bc2: 'type', + 0x8bc5: 'vspace', + 0x8bc6: 'hspace', + 0x978e: 'onerror', } ATTRS7 = { - 0x0001: "href", - 0x03ea: "shape", - 0x03eb: "coords", - 0x03ed: "target", - 0x03ee: "alt", - 0x03ef: "nohref", - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x0001: 'href', + 0x03ea: 'shape', + 0x03eb: 'coords', + 0x03ed: 'target', + 0x03ee: 'alt', + 0x03ef: 'nohref', + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS8 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS9 = { - 0x03ec: "href", - 0x03ed: "target", + 0x03ec: 'href', + 0x03ed: 'target', } ATTRS10 = { - 0x938b: "color", - 0x939b: "face", - 0x93a3: "size", + 0x938b: 'color', + 0x939b: 'face', + 0x93a3: 'size', } ATTRS12 = { - 0x03ea: "src", - 0x03eb: "loop", - 0x03ec: "volume", - 0x03ed: "balance", + 0x03ea: 'src', + 0x03eb: 'loop', + 0x03ec: 'volume', + 0x03ed: 'balance', } ATTRS13 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS15 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x9399: "clear", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x9399: 'clear', } ATTRS16 = { - 0x07db: "link", - 0x07dc: "alink", - 0x07dd: "vlink", - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x938a: "background", - 0x938b: "text", - 0x938e: "nowrap", - 0x93ae: "topmargin", - 0x93af: "rightmargin", - 0x93b0: "bottommargin", - 0x93b1: "leftmargin", - 0x93b6: "bgproperties", - 0x93d8: "scroll", - 0x977b: "onselect", - 0x9791: "onload", - 0x9792: "onunload", - 0x9798: "onbeforeunload", - 0x97b3: "onbeforeprint", - 0x97b4: "onafterprint", - 0xfe0c: "bgcolor", + 0x07db: 'link', + 0x07dc: 'alink', + 0x07dd: 'vlink', + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x938a: 'background', + 0x938b: 'text', + 0x938e: 'nowrap', + 0x93ae: 'topmargin', + 0x93af: 'rightmargin', + 0x93b0: 'bottommargin', + 0x93b1: 'leftmargin', + 0x93b6: 'bgproperties', + 0x93d8: 'scroll', + 0x977b: 'onselect', + 0x9791: 'onload', + 0x9792: 'onunload', + 0x9798: 'onbeforeunload', + 0x97b3: 'onbeforeprint', + 0x97b4: 'onafterprint', + 0xfe0c: 'bgcolor', } ATTRS17 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x9399: "clear", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x9399: 'clear', } ATTRS18 = { - 0x07d1: "type", - 0x8001: "name", + 0x07d1: 'type', + 0x8001: 'name', } ATTRS19 = { - 0x8046: "title", - 0x8049: "align", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x93a8: "valign", + 0x8046: 'title', + 0x8049: 'align', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x93a8: 'valign', } ATTRS20 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x9399: "clear", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x9399: 'clear', } ATTRS21 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS22 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS23 = { - 0x03ea: "span", - 0x8006: "width", - 0x8049: "align", - 0x93a8: "valign", - 0xfe0c: "bgcolor", + 0x03ea: 'span', + 0x8006: 'width', + 0x8049: 'align', + 0x93a8: 'valign', + 0xfe0c: 'bgcolor', } ATTRS24 = { - 0x03ea: "span", - 0x8006: "width", - 0x8049: "align", - 0x93a8: "valign", - 0xfe0c: "bgcolor", + 0x03ea: 'span', + 0x8006: 'width', + 0x8049: 'align', + 0x93a8: 'valign', + 0xfe0c: 'bgcolor', } ATTRS27 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x938e: "nowrap", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x938e: 'nowrap', } ATTRS29 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS31 = { - 0x8046: "title", - 0x8049: "align", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x938e: "nowrap", + 0x8046: 'title', + 0x8049: 'align', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x938e: 'nowrap', } ATTRS32 = { - 0x03ea: "compact", - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x03ea: 'compact', + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS33 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x938e: "nowrap", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x938e: 'nowrap', } ATTRS34 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS35 = { - 0x8001: "name", - 0x8006: "width", - 0x8007: "height", - 0x804a: "align", - 0x8bbd: "palette", - 0x8bbe: "pluginspage", + 0x8001: 'name', + 0x8006: 'width', + 0x8007: 'height', + 0x804a: 'align', + 0x8bbd: 'palette', + 0x8bbe: 'pluginspage', # 0x8bbf: "codebase", - 0x8bbf: "src", - 0x8bc1: "units", - 0x8bc2: "type", - 0x8bc3: "hidden", + 0x8bbf: 'src', + 0x8bc1: 'units', + 0x8bc2: 'type', + 0x8bc3: 'hidden', } ATTRS36 = { - 0x804a: "align", + 0x804a: 'align', } ATTRS37 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x938b: "color", - 0x939b: "face", - 0x939c: "size", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x938b: 'color', + 0x939b: 'face', + 0x939c: 'size', } ATTRS38 = { - 0x03ea: "action", - 0x03ec: "enctype", - 0x03ed: "method", - 0x03ef: "target", - 0x03f4: "accept-charset", - 0x8001: "name", - 0x977c: "onsubmit", - 0x977d: "onreset", + 0x03ea: 'action', + 0x03ec: 'enctype', + 0x03ed: 'method', + 0x03ef: 'target', + 0x03f4: 'accept-charset', + 0x8001: 'name', + 0x977c: 'onsubmit', + 0x977d: 'onreset', } ATTRS39 = { - 0x8000: "align", - 0x8001: "name", - 0x8bb9: "src", - 0x8bbb: "border", - 0x8bbc: "frameborder", - 0x8bbd: "framespacing", - 0x8bbe: "marginwidth", - 0x8bbf: "marginheight", - 0x8bc0: "noresize", - 0x8bc1: "scrolling", - 0x8fa2: "bordercolor", + 0x8000: 'align', + 0x8001: 'name', + 0x8bb9: 'src', + 0x8bbb: 'border', + 0x8bbc: 'frameborder', + 0x8bbd: 'framespacing', + 0x8bbe: 'marginwidth', + 0x8bbf: 'marginheight', + 0x8bc0: 'noresize', + 0x8bc1: 'scrolling', + 0x8fa2: 'bordercolor', } ATTRS40 = { - 0x03e9: "rows", - 0x03ea: "cols", - 0x03eb: "border", - 0x03ec: "bordercolor", - 0x03ed: "frameborder", - 0x03ee: "framespacing", - 0x8001: "name", - 0x9791: "onload", - 0x9792: "onunload", - 0x9798: "onbeforeunload", - 0x97b3: "onbeforeprint", - 0x97b4: "onafterprint", + 0x03e9: 'rows', + 0x03ea: 'cols', + 0x03eb: 'border', + 0x03ec: 'bordercolor', + 0x03ed: 'frameborder', + 0x03ee: 'framespacing', + 0x8001: 'name', + 0x9791: 'onload', + 0x9792: 'onunload', + 0x9798: 'onbeforeunload', + 0x97b3: 'onbeforeprint', + 0x97b4: 'onafterprint', } ATTRS42 = { - 0x8046: "title", - 0x8049: "align", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x9399: "clear", + 0x8046: 'title', + 0x8049: 'align', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x9399: 'clear', } ATTRS43 = { - 0x8046: "title", - 0x8049: "align", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x9399: "clear", + 0x8046: 'title', + 0x8049: 'align', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x9399: 'clear', } ATTRS44 = { - 0x8046: "title", - 0x8049: "align", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x9399: "clear", + 0x8046: 'title', + 0x8049: 'align', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x9399: 'clear', } ATTRS45 = { - 0x8046: "title", - 0x8049: "align", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x9399: "clear", + 0x8046: 'title', + 0x8049: 'align', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x9399: 'clear', } ATTRS46 = { - 0x8046: "title", - 0x8049: "align", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x9399: "clear", + 0x8046: 'title', + 0x8049: 'align', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x9399: 'clear', } ATTRS47 = { - 0x8046: "title", - 0x8049: "align", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x9399: "clear", + 0x8046: 'title', + 0x8049: 'align', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x9399: 'clear', } ATTRS49 = { - 0x03ea: "noshade", - 0x8006: "width", - 0x8007: "size", - 0x8046: "title", - 0x8049: "align", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x938b: "color", + 0x03ea: 'noshade', + 0x8006: 'width', + 0x8007: 'size', + 0x8046: 'title', + 0x8049: 'align', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x938b: 'color', } ATTRS51 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS52 = { - 0x8001: "name", - 0x8006: "width", - 0x8007: "height", - 0x804a: "align", - 0x8bb9: "src", - 0x8bbb: "border", - 0x8bbc: "frameborder", - 0x8bbd: "framespacing", - 0x8bbe: "marginwidth", - 0x8bbf: "marginheight", - 0x8bc0: "noresize", - 0x8bc1: "scrolling", - 0x8fa2: "vspace", - 0x8fa3: "hspace", + 0x8001: 'name', + 0x8006: 'width', + 0x8007: 'height', + 0x804a: 'align', + 0x8bb9: 'src', + 0x8bbb: 'border', + 0x8bbc: 'frameborder', + 0x8bbd: 'framespacing', + 0x8bbe: 'marginwidth', + 0x8bbf: 'marginheight', + 0x8bc0: 'noresize', + 0x8bc1: 'scrolling', + 0x8fa2: 'vspace', + 0x8fa3: 'hspace', } ATTRS53 = { - 0x03eb: "alt", - 0x03ec: "src", - 0x03ed: "border", - 0x03ee: "vspace", - 0x03ef: "hspace", - 0x03f0: "lowsrc", - 0x03f1: "vrml", - 0x03f2: "dynsrc", - 0x03f4: "loop", - 0x03f6: "start", - 0x07d3: "ismap", - 0x07d9: "usemap", - 0x8001: "name", - 0x8006: "width", - 0x8007: "height", - 0x8046: "title", - 0x804a: "align", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x978d: "onabort", - 0x978e: "onerror", - 0x9791: "onload", + 0x03eb: 'alt', + 0x03ec: 'src', + 0x03ed: 'border', + 0x03ee: 'vspace', + 0x03ef: 'hspace', + 0x03f0: 'lowsrc', + 0x03f1: 'vrml', + 0x03f2: 'dynsrc', + 0x03f4: 'loop', + 0x03f6: 'start', + 0x07d3: 'ismap', + 0x07d9: 'usemap', + 0x8001: 'name', + 0x8006: 'width', + 0x8007: 'height', + 0x8046: 'title', + 0x804a: 'align', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x978d: 'onabort', + 0x978e: 'onerror', + 0x9791: 'onload', } ATTRS54 = { - 0x07d1: "type", - 0x07d3: "size", - 0x07d4: "maxlength", - 0x07d6: "readonly", - 0x07d8: "indeterminate", - 0x07da: "checked", - 0x07db: "alt", - 0x07dc: "src", - 0x07dd: "border", - 0x07de: "vspace", - 0x07df: "hspace", - 0x07e0: "lowsrc", - 0x07e1: "vrml", - 0x07e2: "dynsrc", - 0x07e4: "loop", - 0x07e5: "start", - 0x8001: "name", - 0x8006: "width", - 0x8007: "height", - 0x804a: "align", - 0x93ee: "value", - 0x977b: "onselect", - 0x978d: "onabort", - 0x978e: "onerror", - 0x978f: "onchange", - 0x9791: "onload", + 0x07d1: 'type', + 0x07d3: 'size', + 0x07d4: 'maxlength', + 0x07d6: 'readonly', + 0x07d8: 'indeterminate', + 0x07da: 'checked', + 0x07db: 'alt', + 0x07dc: 'src', + 0x07dd: 'border', + 0x07de: 'vspace', + 0x07df: 'hspace', + 0x07e0: 'lowsrc', + 0x07e1: 'vrml', + 0x07e2: 'dynsrc', + 0x07e4: 'loop', + 0x07e5: 'start', + 0x8001: 'name', + 0x8006: 'width', + 0x8007: 'height', + 0x804a: 'align', + 0x93ee: 'value', + 0x977b: 'onselect', + 0x978d: 'onabort', + 0x978e: 'onerror', + 0x978f: 'onchange', + 0x9791: 'onload', } ATTRS56 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS57 = { - 0x03e9: "for", + 0x03e9: 'for', } ATTRS58 = { - 0x804a: "align", + 0x804a: 'align', } ATTRS59 = { - 0x03ea: "value", - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x939a: "type", + 0x03ea: 'value', + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x939a: 'type', } ATTRS60 = { - 0x03ee: "href", - 0x03ef: "rel", - 0x03f0: "rev", - 0x03f1: "type", - 0x03f9: "media", - 0x03fa: "target", - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x978e: "onerror", - 0x9791: "onload", + 0x03ee: 'href', + 0x03ef: 'rel', + 0x03f0: 'rev', + 0x03f1: 'type', + 0x03f9: 'media', + 0x03fa: 'target', + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x978e: 'onerror', + 0x9791: 'onload', } ATTRS61 = { - 0x9399: "clear", + 0x9399: 'clear', } ATTRS62 = { - 0x8001: "name", - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8001: 'name', + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS63 = { - 0x1771: "scrolldelay", - 0x1772: "direction", - 0x1773: "behavior", - 0x1774: "scrollamount", - 0x1775: "loop", - 0x1776: "vspace", - 0x1777: "hspace", - 0x1778: "truespeed", - 0x8006: "width", - 0x8007: "height", - 0x9785: "onbounce", - 0x978b: "onfinish", - 0x978c: "onstart", - 0xfe0c: "bgcolor", + 0x1771: 'scrolldelay', + 0x1772: 'direction', + 0x1773: 'behavior', + 0x1774: 'scrollamount', + 0x1775: 'loop', + 0x1776: 'vspace', + 0x1777: 'hspace', + 0x1778: 'truespeed', + 0x8006: 'width', + 0x8007: 'height', + 0x9785: 'onbounce', + 0x978b: 'onfinish', + 0x978c: 'onstart', + 0xfe0c: 'bgcolor', } ATTRS65 = { - 0x03ea: "http-equiv", - 0x03eb: "content", - 0x03ec: "url", - 0x03f6: "charset", - 0x8001: "name", + 0x03ea: 'http-equiv', + 0x03eb: 'content', + 0x03ec: 'url', + 0x03f6: 'charset', + 0x8001: 'name', } ATTRS66 = { - 0x03f5: "n", + 0x03f5: 'n', } ATTRS71 = { # 0x8000: "border", - 0x8000: "usemap", - 0x8001: "name", - 0x8006: "width", - 0x8007: "height", - 0x8046: "title", - 0x804a: "align", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x8bbb: "classid", - 0x8bbc: "data", - 0x8bbf: "codebase", - 0x8bc0: "codetype", - 0x8bc1: "code", - 0x8bc2: "type", - 0x8bc5: "vspace", - 0x8bc6: "hspace", - 0x978e: "onerror", + 0x8000: 'usemap', + 0x8001: 'name', + 0x8006: 'width', + 0x8007: 'height', + 0x8046: 'title', + 0x804a: 'align', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x8bbb: 'classid', + 0x8bbc: 'data', + 0x8bbf: 'codebase', + 0x8bc0: 'codetype', + 0x8bc1: 'code', + 0x8bc2: 'type', + 0x8bc5: 'vspace', + 0x8bc6: 'hspace', + 0x978e: 'onerror', } ATTRS72 = { - 0x03eb: "compact", - 0x03ec: "start", - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x939a: "type", + 0x03eb: 'compact', + 0x03ec: 'start', + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x939a: 'type', } ATTRS73 = { - 0x03ea: "selected", - 0x03eb: "value", + 0x03ea: 'selected', + 0x03eb: 'value', } ATTRS74 = { - 0x8046: "title", - 0x8049: "align", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x9399: "clear", + 0x8046: 'title', + 0x8049: 'align', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x9399: 'clear', } ATTRS75 = { # 0x8000: "name", # 0x8000: "value", - 0x8000: "type", + 0x8000: 'type', } ATTRS76 = { - 0x9399: "clear", + 0x9399: 'clear', } ATTRS77 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x9399: "clear", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x9399: 'clear', } ATTRS78 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS82 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS83 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS84 = { - 0x03ea: "src", - 0x03ed: "for", - 0x03ee: "event", - 0x03f0: "defer", - 0x03f2: "type", - 0x978e: "onerror", + 0x03ea: 'src', + 0x03ed: 'for', + 0x03ee: 'event', + 0x03f0: 'defer', + 0x03f2: 'type', + 0x978e: 'onerror', } ATTRS85 = { - 0x03eb: "size", - 0x03ec: "multiple", - 0x8000: "align", - 0x8001: "name", - 0x978f: "onchange", + 0x03eb: 'size', + 0x03ec: 'multiple', + 0x8000: 'align', + 0x8001: 'name', + 0x978f: 'onchange', } ATTRS86 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS87 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS88 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS89 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS90 = { - 0x03eb: "type", - 0x03ef: "media", - 0x8046: "title", - 0x978e: "onerror", - 0x9791: "onload", + 0x03eb: 'type', + 0x03ef: 'media', + 0x8046: 'title', + 0x978e: 'onerror', + 0x9791: 'onload', } ATTRS91 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS92 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS93 = { - 0x03ea: "cols", - 0x03eb: "border", - 0x03ec: "rules", - 0x03ed: "frame", - 0x03ee: "cellspacing", - 0x03ef: "cellpadding", - 0x03fa: "datapagesize", - 0x8006: "width", - 0x8007: "height", - 0x8046: "title", - 0x804a: "align", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x938a: "background", - 0x93a5: "bordercolor", - 0x93a6: "bordercolorlight", - 0x93a7: "bordercolordark", - 0xfe0c: "bgcolor", + 0x03ea: 'cols', + 0x03eb: 'border', + 0x03ec: 'rules', + 0x03ed: 'frame', + 0x03ee: 'cellspacing', + 0x03ef: 'cellpadding', + 0x03fa: 'datapagesize', + 0x8006: 'width', + 0x8007: 'height', + 0x8046: 'title', + 0x804a: 'align', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x938a: 'background', + 0x93a5: 'bordercolor', + 0x93a6: 'bordercolorlight', + 0x93a7: 'bordercolordark', + 0xfe0c: 'bgcolor', } ATTRS94 = { - 0x8049: "align", - 0x93a8: "valign", - 0xfe0c: "bgcolor", + 0x8049: 'align', + 0x93a8: 'valign', + 0xfe0c: 'bgcolor', } ATTRS95 = { - 0x8049: "align", - 0x93a8: "valign", + 0x8049: 'align', + 0x93a8: 'valign', } ATTRS96 = { - 0x07d2: "rowspan", - 0x07d3: "colspan", - 0x8006: "width", - 0x8007: "height", - 0x8046: "title", - 0x8049: "align", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x938a: "background", - 0x938e: "nowrap", - 0x93a5: "bordercolor", - 0x93a6: "bordercolorlight", - 0x93a7: "bordercolordark", - 0x93a8: "valign", - 0xfe0c: "bgcolor", + 0x07d2: 'rowspan', + 0x07d3: 'colspan', + 0x8006: 'width', + 0x8007: 'height', + 0x8046: 'title', + 0x8049: 'align', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x938a: 'background', + 0x938e: 'nowrap', + 0x93a5: 'bordercolor', + 0x93a6: 'bordercolorlight', + 0x93a7: 'bordercolordark', + 0x93a8: 'valign', + 0xfe0c: 'bgcolor', } ATTRS97 = { - 0x1b5a: "rows", - 0x1b5b: "cols", - 0x1b5c: "wrap", - 0x1b5d: "readonly", - 0x8001: "name", - 0x977b: "onselect", - 0x978f: "onchange", + 0x1b5a: 'rows', + 0x1b5b: 'cols', + 0x1b5c: 'wrap', + 0x1b5d: 'readonly', + 0x8001: 'name', + 0x977b: 'onselect', + 0x978f: 'onchange', } ATTRS98 = { - 0x8049: "align", - 0x93a8: "valign", - 0xfe0c: "bgcolor", + 0x8049: 'align', + 0x93a8: 'valign', + 0xfe0c: 'bgcolor', } ATTRS99 = { - 0x07d2: "rowspan", - 0x07d3: "colspan", - 0x8006: "width", - 0x8007: "height", - 0x8046: "title", - 0x8049: "align", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x938a: "background", - 0x938e: "nowrap", - 0x93a5: "bordercolor", - 0x93a6: "bordercolorlight", - 0x93a7: "bordercolordark", - 0x93a8: "valign", - 0xfe0c: "bgcolor", + 0x07d2: 'rowspan', + 0x07d3: 'colspan', + 0x8006: 'width', + 0x8007: 'height', + 0x8046: 'title', + 0x8049: 'align', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x938a: 'background', + 0x938e: 'nowrap', + 0x93a5: 'bordercolor', + 0x93a6: 'bordercolorlight', + 0x93a7: 'bordercolordark', + 0x93a8: 'valign', + 0xfe0c: 'bgcolor', } ATTRS100 = { - 0x8049: "align", - 0x93a8: "valign", - 0xfe0c: "bgcolor", + 0x8049: 'align', + 0x93a8: 'valign', + 0xfe0c: 'bgcolor', } ATTRS102 = { - 0x8007: "height", - 0x8046: "title", - 0x8049: "align", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x93a5: "bordercolor", - 0x93a6: "bordercolorlight", - 0x93a7: "bordercolordark", - 0x93a8: "valign", - 0xfe0c: "bgcolor", + 0x8007: 'height', + 0x8046: 'title', + 0x8049: 'align', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x93a5: 'bordercolor', + 0x93a6: 'bordercolorlight', + 0x93a7: 'bordercolordark', + 0x93a8: 'valign', + 0xfe0c: 'bgcolor', } ATTRS103 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS104 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS105 = { - 0x03eb: "compact", - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", - 0x939a: "type", + 0x03eb: 'compact', + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', + 0x939a: 'type', } ATTRS106 = { - 0x8046: "title", - 0x804b: "style", - 0x83ea: "class", - 0x83eb: "id", + 0x8046: 'title', + 0x804b: 'style', + 0x83ea: 'class', + 0x83eb: 'id', } ATTRS108 = { - 0x9399: "clear", + 0x9399: 'clear', } TAGS_ATTRS = [ diff --git a/src/calibre/ebooks/lit/maps/opf.py b/src/calibre/ebooks/lit/maps/opf.py index af1355612b..8247ebb326 100644 --- a/src/calibre/ebooks/lit/maps/opf.py +++ b/src/calibre/ebooks/lit/maps/opf.py @@ -1,16 +1,16 @@ __license__ = 'GPL v3' __copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>' -""" +''' Microsoft LIT OPF tag and attribute tables, copied from ConvertLIT. -""" +''' TAGS = [ None, - "package", - "dc:Title", - "dc:Creator", + 'package', + 'dc:Title', + 'dc:Creator', None, None, None, @@ -23,58 +23,58 @@ TAGS = [ None, None, None, - "manifest", - "item", - "spine", - "itemref", - "metadata", - "dc-metadata", - "dc:Subject", - "dc:Description", - "dc:Publisher", - "dc:Contributor", - "dc:Date", - "dc:Type", - "dc:Format", - "dc:Identifier", - "dc:Source", - "dc:Language", - "dc:Relation", - "dc:Coverage", - "dc:Rights", - "x-metadata", - "meta", - "tours", - "tour", - "site", - "guide", - "reference", + 'manifest', + 'item', + 'spine', + 'itemref', + 'metadata', + 'dc-metadata', + 'dc:Subject', + 'dc:Description', + 'dc:Publisher', + 'dc:Contributor', + 'dc:Date', + 'dc:Type', + 'dc:Format', + 'dc:Identifier', + 'dc:Source', + 'dc:Language', + 'dc:Relation', + 'dc:Coverage', + 'dc:Rights', + 'x-metadata', + 'meta', + 'tours', + 'tour', + 'site', + 'guide', + 'reference', None, ] ATTRS = { - 0x0001: "href", - 0x0002: "%never-used", - 0x0003: "%guid", - 0x0004: "%minimum_level", - 0x0005: "%attr5", - 0x0006: "id", - 0x0007: "href", - 0x0008: "media-type", - 0x0009: "fallback", - 0x000A: "idref", - 0x000B: "xmlns:dc", - 0x000C: "xmlns:oebpackage", - 0x000D: "role", - 0x000E: "file-as", - 0x000F: "event", - 0x0010: "scheme", - 0x0011: "title", - 0x0012: "type", - 0x0013: "unique-identifier", - 0x0014: "name", - 0x0015: "content", - 0x0016: "xml:lang", + 0x0001: 'href', + 0x0002: '%never-used', + 0x0003: '%guid', + 0x0004: '%minimum_level', + 0x0005: '%attr5', + 0x0006: 'id', + 0x0007: 'href', + 0x0008: 'media-type', + 0x0009: 'fallback', + 0x000A: 'idref', + 0x000B: 'xmlns:dc', + 0x000C: 'xmlns:oebpackage', + 0x000D: 'role', + 0x000E: 'file-as', + 0x000F: 'event', + 0x0010: 'scheme', + 0x0011: 'title', + 0x0012: 'type', + 0x0013: 'unique-identifier', + 0x0014: 'name', + 0x0015: 'content', + 0x0016: 'xml:lang', } TAGS_ATTRS = [{} for i in range(43)] diff --git a/src/calibre/ebooks/lit/mssha1.py b/src/calibre/ebooks/lit/mssha1.py index ccd735cd94..b1a8419d4b 100644 --- a/src/calibre/ebooks/lit/mssha1.py +++ b/src/calibre/ebooks/lit/mssha1.py @@ -1,8 +1,8 @@ -""" +''' Modified version of SHA-1 used in Microsoft LIT files. Adapted from the PyPy pure-Python SHA-1 implementation. -""" +''' __license__ = 'GPL v3' __copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>' @@ -21,12 +21,12 @@ from polyglot.builtins import long_type def _long2bytesBigEndian(n, blocksize=0): - """Convert a long integer to a byte string. + '''Convert a long integer to a byte string. If optional blocksize is given and greater than zero, pad the front of the byte string with binary zeros so that the length is a multiple of blocksize. - """ + ''' # After much testing, this algorithm was deemed to be the fastest. s = b'' @@ -47,7 +47,7 @@ def _long2bytesBigEndian(n, blocksize=0): def _bytelist2longBigEndian(blist): - "Transform a list of characters into a list of longs." + 'Transform a list of characters into a list of longs.' imax = len(blist)//4 hl = [0] * imax @@ -67,7 +67,7 @@ def _bytelist2longBigEndian(blist): def _rotateLeft(x, n): - "Rotate x (32 bit) left n bits circular." + 'Rotate x (32 bit) left n bits circular.' return (x << n) | (x >> (32-n)) @@ -123,10 +123,10 @@ K = [ class mssha1: - "An implementation of the MD5 hash function in pure Python." + 'An implementation of the MD5 hash function in pure Python.' def __init__(self): - "Initialisation." + 'Initialisation.' # Initial message length in bits(!). self.length = 0 @@ -140,7 +140,7 @@ class mssha1: self.init() def init(self): - "Initialize the message-digest and set all fields to zero." + 'Initialize the message-digest and set all fields to zero.' self.length = 0 self.input = [] @@ -182,7 +182,7 @@ class mssha1: # API of the sha module. def update(self, inBuf): - """Add to the current message. + '''Add to the current message. Update the mssha1 object with the string arg. Repeated calls are equivalent to a single call with the concatenation of all @@ -195,7 +195,7 @@ class mssha1: keep an intermediate value for the hash, so that we only need to make minimal recalculation if we call update() to add more data to the hashed string. - """ + ''' inBuf = bytearray(inBuf) leninBuf = long_type(len(inBuf)) @@ -225,12 +225,12 @@ class mssha1: self.input = self.input + inBuf def digest(self): - """Terminate the message-digest computation and return digest. + '''Terminate the message-digest computation and return digest. Return the digest of the strings passed to the update() method so far. This is a 16-byte string which may contain non-ASCII characters, including null bytes. - """ + ''' H0 = self.H0 H1 = self.H1 @@ -273,13 +273,13 @@ class mssha1: return digest def hexdigest(self): - """Terminate and return digest in HEX form. + '''Terminate and return digest in HEX form. Like digest() except the digest is returned as a string of length 32, containing only hexadecimal digits. This may be used to exchange the value safely in email or other non- binary environments. - """ + ''' return ''.join(['%02x' % c for c in bytearray(self.digest())]) def copy(self): @@ -306,10 +306,10 @@ blocksize = 1 def new(arg=None): - """Return a new mssha1 crypto object. + '''Return a new mssha1 crypto object. If arg is present, the method call update(arg) is made. - """ + ''' crypto = mssha1() if arg: @@ -323,7 +323,7 @@ if __name__ == '__main__': import sys file = None if len(sys.argv) > 2: - print("usage: %s [FILE]" % sys.argv[0]) + print('usage: %s [FILE]' % sys.argv[0]) return elif len(sys.argv) < 2: file = sys.stdin diff --git a/src/calibre/ebooks/lit/reader.py b/src/calibre/ebooks/lit/reader.py index a0899a7491..c9c513954c 100644 --- a/src/calibre/ebooks/lit/reader.py +++ b/src/calibre/ebooks/lit/reader.py @@ -25,23 +25,23 @@ from polyglot.builtins import codepoint_to_chr, itervalues, string_or_bytes from polyglot.urllib import unquote as urlunquote from polyglot.urllib import urldefrag -__all__ = ["LitReader"] +__all__ = ['LitReader'] -XML_DECL = """<?xml version="1.0" encoding="UTF-8" ?> -""" -OPF_DECL = """<?xml version="1.0" encoding="UTF-8" ?> +XML_DECL = '''<?xml version="1.0" encoding="UTF-8" ?> +''' +OPF_DECL = '''<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE package PUBLIC "+//ISBN 0-9673008-1-9//DTD OEB 1.0.1 Package//EN" "http://openebook.org/dtds/oeb-1.0.1/oebpkg101.dtd"> -""" -HTML_DECL = """<?xml version="1.0" encoding="UTF-8" ?> +''' +HTML_DECL = '''<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE html PUBLIC "+//ISBN 0-9673008-1-9//DTD OEB 1.0.1 Document//EN" "http://openebook.org/dtds/oeb-1.0.1/oebdoc101.dtd"> -""" +''' -DESENCRYPT_GUID = "{67F6E4A2-60BF-11D3-8540-00C04F58C3CF}" -LZXCOMPRESS_GUID = "{0A9007C6-4076-11D3-8789-0000F8105754}" +DESENCRYPT_GUID = '{67F6E4A2-60BF-11D3-8540-00C04F58C3CF}' +LZXCOMPRESS_GUID = '{0A9007C6-4076-11D3-8789-0000F8105754}' CONTROL_TAG = 4 CONTROL_WINDOW_SIZE = 12 @@ -84,8 +84,8 @@ def encint(byts, remaining): def msguid(bytes): - values = struct.unpack("<LHHBBBBBBBB", bytes[:16]) - return "{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}" % values + values = struct.unpack('<LHHBBBBBBBB', bytes[:16]) + return '{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}' % values def read_utf8_char(bytes, pos): @@ -242,7 +242,7 @@ class UnBinary: if flags & FLAG_ATOM: if not self.tag_atoms or tag not in self.tag_atoms: raise LitError( - "atom tag %d not in atom tag list" % tag) + 'atom tag %d not in atom tag list' % tag) tag_name = self.tag_atoms[tag] current_map = self.attr_atoms elif tag < len(self.tag_map): @@ -397,7 +397,7 @@ class DirectoryEntry: self.size = size def __repr__(self): - return "DirectoryEntry(name=%s, section=%d, offset=%d, size=%d)" \ + return 'DirectoryEntry(name=%s, section=%d, offset=%d, size=%d)' \ % (repr(self.name), self.section, self.offset, self.size) def __str__(self): @@ -429,8 +429,8 @@ class ManifestItem: return self.internal == other def __repr__(self): - return "ManifestItem(internal=%r, path=%r, mime_type=%r, " \ - "offset=%d, root=%r, state=%r)" \ + return 'ManifestItem(internal=%r, path=%r, mime_type=%r, ' \ + 'offset=%d, root=%r, state=%r)' \ % (self.internal, self.path, self.mime_type, self.offset, self.root, self.state) @@ -649,7 +649,7 @@ class LitFile: raise LitError('Invalid Namelist section') pos = 4 num_sections = u16(raw[2:pos]) - self.section_names = [""] * num_sections + self.section_names = [''] * num_sections self.section_data = [None] * num_sections for section in range(num_sections): size = u16(raw[pos:pos+2]) @@ -698,7 +698,7 @@ class LitFile: path = item.path while shared and not path.startswith(shared): try: - shared = shared[:shared.rindex("/", 0, -2) + 1] + shared = shared[:shared.rindex('/', 0, -2) + 1] except ValueError: shared = None if not shared: @@ -730,7 +730,7 @@ class LitFile: raise LitError('Unable to decrypt title key!') self.bookkey = bookkey[1:9] else: - raise DRMError("Cannot access DRM-protected book") + raise DRMError('Cannot access DRM-protected book') def calculate_deskey(self): hashfiles = ['/meta', '/DRMStorage/DRMSource'] @@ -741,11 +741,11 @@ class LitFile: for name in hashfiles: data = self.get_file(name) if prepad > 0: - data = (b"\000" * prepad) + data + data = (b'\000' * prepad) + data prepad = 0 postpad = 64 - (len(data) % 64) if postpad < 64: - data = data + (b"\000" * postpad) + data = data + (b'\000' * postpad) hash.update(data) digest = hash.digest() if not isinstance(digest, bytes): @@ -779,7 +779,7 @@ class LitFile: while len(transform) >= 16: csize = (int32(control) + 1) * 4 if csize > len(control) or csize <= 0: - raise LitError("ControlData is too short") + raise LitError('ControlData is too short') guid = msguid(transform) if guid == DESENCRYPT_GUID: content = self.decrypt(content) @@ -791,7 +791,7 @@ class LitFile: content = self.decompress(content, control, reset_table) control = control[csize:] else: - raise LitError("Unrecognized transform: %s." % repr(guid)) + raise LitError('Unrecognized transform: %s.' % repr(guid)) transform = transform[16:] return content @@ -799,18 +799,18 @@ class LitFile: length = len(content) extra = length & 0x7 if extra > 0: - self.warn("content length not a multiple of block size") - content += b"\0" * (8 - extra) + self.warn('content length not a multiple of block size') + content += b'\0' * (8 - extra) msdes.deskey(self.bookkey, msdes.DE1) return msdes.des(content) def decompress(self, content, control, reset_table): - if len(control) < 32 or control[CONTROL_TAG:CONTROL_TAG+4] != b"LZXC": - raise LitError("Invalid ControlData tag value") + if len(control) < 32 or control[CONTROL_TAG:CONTROL_TAG+4] != b'LZXC': + raise LitError('Invalid ControlData tag value') if len(reset_table) < (RESET_INTERVAL + 8): - raise LitError("Reset table is too short") + raise LitError('Reset table is too short') if u32(reset_table[RESET_UCLENGTH + 4:]) != 0: - raise LitError("Reset table has 64bit value for UCLENGTH") + raise LitError('Reset table has 64bit value for UCLENGTH') result = [] @@ -820,7 +820,7 @@ class LitFile: u >>= 1 window_size += 1 if window_size < 15 or window_size > 21: - raise LitError("Invalid window in ControlData") + raise LitError('Invalid window in ControlData') lzx.init(window_size) ofs_entry = int32(reset_table[RESET_HDRLEN:]) + 8 @@ -836,16 +836,16 @@ class LitFile: size = int32(reset_table[ofs_entry:]) u = int32(reset_table[ofs_entry + 4:]) if u != 0: - raise LitError("Reset table entry greater than 32 bits") + raise LitError('Reset table entry greater than 32 bits') if size >= len(content): - self._warn("LZX reset table entry out of bounds") + self._warn('LZX reset table entry out of bounds') if bytes_remaining >= window_bytes: lzx.reset() try: result.append( lzx.decompress(content[base:size], window_bytes)) except lzx.LZXError: - self.warn("LZX decompression error; skipping chunk") + self.warn('LZX decompression error; skipping chunk') bytes_remaining -= window_bytes base = size accum += int32(reset_table[RESET_INTERVAL:]) @@ -855,10 +855,10 @@ class LitFile: try: result.append(lzx.decompress(content[base:], bytes_remaining)) except lzx.LZXError: - self.warn("LZX decompression error; skipping chunk") + self.warn('LZX decompression error; skipping chunk') bytes_remaining = 0 if bytes_remaining > 0: - raise LitError("Failed to completely decompress section") + raise LitError('Failed to completely decompress section') return b''.join(result) def get_atoms(self, entry): @@ -876,7 +876,7 @@ class LitFile: break tags[i], data = data[:size], data[size:] if len(tags) != nentries: - self._warn("damaged or invalid atoms tag table") + self._warn('damaged or invalid atoms tag table') if len(data) < 4: return (tags, {}) attrs = {} @@ -889,12 +889,12 @@ class LitFile: break attrs[i], data = data[:size], data[size:] if len(attrs) != nentries: - self._warn("damaged or invalid atoms attributes table") + self._warn('damaged or invalid atoms attributes table') return (tags, attrs) class LitContainer: - """Simple Container-interface, read-only accessor for LIT files.""" + '''Simple Container-interface, read-only accessor for LIT files.''' def __init__(self, filename_or_stream, log): self._litfile = LitFile(filename_or_stream, log) @@ -934,7 +934,7 @@ class LitContainer: except LitError: if b'PENGUIN group' not in raw: raise - print("WARNING: attempting PENGUIN malformed OPF fix") + print('WARNING: attempting PENGUIN malformed OPF fix') raw = raw.replace( b'PENGUIN group', b'\x00\x01\x18\x00PENGUIN group', 1) unbin = UnBinary(raw, path, self._litfile.manifest, OPF_MAP) diff --git a/src/calibre/ebooks/lit/writer.py b/src/calibre/ebooks/lit/writer.py index 044f88d340..52ec2f44a9 100644 --- a/src/calibre/ebooks/lit/writer.py +++ b/src/calibre/ebooks/lit/writer.py @@ -61,11 +61,11 @@ HTML_MAP = invert_tag_map(maps.HTML_MAP) LIT_MAGIC = b'ITOLITLS' -LITFILE_GUID = "{0A9007C1-4076-11D3-8789-0000F8105754}" -PIECE3_GUID = "{0A9007C3-4076-11D3-8789-0000F8105754}" -PIECE4_GUID = "{0A9007C4-4076-11D3-8789-0000F8105754}" -DESENCRYPT_GUID = "{67F6E4A2-60BF-11D3-8540-00C04F58C3CF}" -LZXCOMPRESS_GUID = "{0A9007C6-4076-11D3-8789-0000F8105754}" +LITFILE_GUID = '{0A9007C1-4076-11D3-8789-0000F8105754}' +PIECE3_GUID = '{0A9007C3-4076-11D3-8789-0000F8105754}' +PIECE4_GUID = '{0A9007C4-4076-11D3-8789-0000F8105754}' +DESENCRYPT_GUID = '{67F6E4A2-60BF-11D3-8540-00C04F58C3CF}' +LZXCOMPRESS_GUID = '{0A9007C6-4076-11D3-8789-0000F8105754}' def packguid(guid): @@ -73,7 +73,7 @@ def packguid(guid): guid[20:22], guid[22:24], guid[25:27], guid[27:29], \ guid[29:31], guid[31:33], guid[33:35], guid[35:37] values = [int(value, 16) for value in values] - return pack("<LHHBBBBBBBB", *values) + return pack('<LHHBBBBBBBB', *values) FLAG_OPENING = (1 << 0) @@ -94,24 +94,24 @@ ROOT_OFFSET = 1284508585713721976 ROOT_SIZE = 4165955342166943123 BLOCK_CAOL = \ - b"\x43\x41\x4f\x4c\x02\x00\x00\x00" \ - b"\x50\x00\x00\x00\x37\x13\x03\x00" \ - b"\x00\x00\x00\x00\x00\x20\x00\x00" \ - b"\x00\x02\x00\x00\x00\x00\x10\x00" \ - b"\x00\x00\x02\x00\x00\x00\x00\x00" \ - b"\x00\x00\x00\x00\x00\x00\x00\x00" + b'\x43\x41\x4f\x4c\x02\x00\x00\x00' \ + b'\x50\x00\x00\x00\x37\x13\x03\x00' \ + b'\x00\x00\x00\x00\x00\x20\x00\x00' \ + b'\x00\x02\x00\x00\x00\x00\x10\x00' \ + b'\x00\x00\x02\x00\x00\x00\x00\x00' \ + b'\x00\x00\x00\x00\x00\x00\x00\x00' BLOCK_ITSF = \ - b"\x49\x54\x53\x46\x04\x00\x00\x00" \ - b"\x20\x00\x00\x00\x01\x00\x00\x00" + b'\x49\x54\x53\x46\x04\x00\x00\x00' \ + b'\x20\x00\x00\x00\x01\x00\x00\x00' MSDES_CONTROL = \ - b"\x03\x00\x00\x00\x29\x17\x00\x00" \ - b"\x01\x00\x00\x00\xa5\xa5\x00\x00" + b'\x03\x00\x00\x00\x29\x17\x00\x00' \ + b'\x01\x00\x00\x00\xa5\xa5\x00\x00' LZXC_CONTROL = \ - b"\x07\x00\x00\x00\x4c\x5a\x58\x43" \ - b"\x03\x00\x00\x00\x04\x00\x00\x00" \ - b"\x04\x00\x00\x00\x02\x00\x00\x00" \ - b"\x00\x00\x00\x00\x00\x00\x00\x00" + b'\x07\x00\x00\x00\x4c\x5a\x58\x43' \ + b'\x03\x00\x00\x00\x04\x00\x00\x00' \ + b'\x04\x00\x00\x00\x02\x00\x00\x00' \ + b'\x00\x00\x00\x00\x00\x00\x00\x00' COLLAPSE = re.compile(r'[ \t\r\n\v]+') @@ -275,8 +275,8 @@ class ReBinary: def build_ahc(self): if len(self.anchors) > 6: - self.logger.warn("More than six anchors in file %r. " - "Some links may not work properly." % self.item.href) + self.logger.warn('More than six anchors in file %r. ' + 'Some links may not work properly.' % self.item.href) data = io.BytesIO() data.write(codepoint_to_chr(len(self.anchors)).encode('utf-8')) for anchor, offset in self.anchors: @@ -469,8 +469,8 @@ class LitWriter: self._add_folder('/data') for item in self._oeb.manifest.values(): if item.media_type not in LIT_MIMES: - self._logger.warn("File %r of unknown media-type %r " - "excluded from output." % (item.href, item.media_type)) + self._logger.warn('File %r of unknown media-type %r ' + 'excluded from output.' % (item.href, item.media_type)) continue name = '/data/' + item.id data = item.data @@ -579,7 +579,7 @@ class LitWriter: self._add_file('/DRMStorage/DRMSource', drmsource) tempkey = self._calculate_deskey([self._meta, drmsource]) msdes.deskey(tempkey, msdes.EN0) - self._add_file('/DRMStorage/DRMSealed', msdes.des(b"\0" * 16)) + self._add_file('/DRMStorage/DRMSealed', msdes.des(b'\0' * 16)) self._bookkey = b'\0' * 8 self._add_file('/DRMStorage/ValidationStream', b'MSReader', 3) @@ -651,11 +651,11 @@ class LitWriter: hash = mssha1.new() for data in hashdata: if prepad > 0: - data = (b"\000" * prepad) + data + data = (b'\000' * prepad) + data prepad = 0 postpad = 64 - (len(data) % 64) if postpad < 64: - data = data + (b"\000" * postpad) + data = data + (b'\000' * postpad) hash.update(data) digest = hash.digest() if not isinstance(digest, bytes): diff --git a/src/calibre/ebooks/lrf/__init__.py b/src/calibre/ebooks/lrf/__init__.py index 4a324b3f40..73dad92c06 100644 --- a/src/calibre/ebooks/lrf/__init__.py +++ b/src/calibre/ebooks/lrf/__init__.py @@ -1,16 +1,16 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' -""" +''' This package contains logic to read and write LRF files. The LRF file format is documented at U{http://www.sven.de/librie/Librie/LrfFormat}. -""" +''' from calibre.ebooks import ConversionError from calibre.ebooks.lrf.fonts import FONT_FILE_MAP from calibre.ebooks.lrf.pylrs.pylrs import BlockStyle, Header, TextBlock, TextStyle from calibre.ebooks.lrf.pylrs.pylrs import Book as _Book -__docformat__ = "epytext" +__docformat__ = 'epytext' class LRFParseError(Exception): @@ -28,8 +28,8 @@ class PRS500_PROFILE: line_space = 1.2 # : Default (in pt) header_font_size = 6 #: In pt header_height = 30 # : In px - default_fonts = {'sans': "Swis721 BT Roman", 'mono': "Courier10 BT Roman", - 'serif': "Dutch801 Rm BT Roman"} + default_fonts = {'sans': 'Swis721 BT Roman', 'mono': 'Courier10 BT Roman', + 'serif': 'Dutch801 Rm BT Roman'} name = 'prs500' diff --git a/src/calibre/ebooks/lrf/html/__init__.py b/src/calibre/ebooks/lrf/html/__init__.py index 9e110971bf..24cb7ee249 100644 --- a/src/calibre/ebooks/lrf/html/__init__.py +++ b/src/calibre/ebooks/lrf/html/__init__.py @@ -1,8 +1,8 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' -""" +''' This package contains code to convert HTML ebooks to LRF ebooks. -""" +''' -__docformat__ = "epytext" -__author__ = "Kovid Goyal <kovid@kovidgoyal.net>" +__docformat__ = 'epytext' +__author__ = 'Kovid Goyal <kovid@kovidgoyal.net>' diff --git a/src/calibre/ebooks/lrf/html/convert_from.py b/src/calibre/ebooks/lrf/html/convert_from.py index a15cc5ce7a..fea851f502 100644 --- a/src/calibre/ebooks/lrf/html/convert_from.py +++ b/src/calibre/ebooks/lrf/html/convert_from.py @@ -46,12 +46,12 @@ from calibre.ptempfile import PersistentTemporaryFile from polyglot.builtins import itervalues, string_or_bytes from polyglot.urllib import unquote, urlparse -""" +''' Code to convert HTML ebooks into LRF ebooks. I am indebted to esperanc for the initial CSS->Xylog Style conversion code and to Falstaff for pylrs. -""" +''' from PIL import Image as PILImage @@ -98,7 +98,7 @@ def tag_regex(tagname): class HTMLConverter: - SELECTOR_PAT = re.compile(r"([A-Za-z0-9\-\_\:\.]+[A-Za-z0-9\-\_\:\.\s\,]*)\s*\{([^\}]*)\}") + SELECTOR_PAT = re.compile(r'([A-Za-z0-9\-\_\:\.]+[A-Za-z0-9\-\_\:\.\s\,]*)\s*\{([^\}]*)\}') PAGE_BREAK_PAT = re.compile(r'page-break-(?:after|before)\s*:\s*(\w+)', re.IGNORECASE) IGNORED_TAGS = (Comment, Declaration, ProcessingInstruction) @@ -108,7 +108,7 @@ class HTMLConverter: lambda match: '<a'+match.group(1)+'></a>'), # Strip comments from <style> tags. This is needed as # sometimes there are unterminated comments - (re.compile(r"<\s*style.*?>(.*?)<\/\s*style\s*>", re.DOTALL|re.IGNORECASE), + (re.compile(r'<\s*style.*?>(.*?)<\/\s*style\s*>', re.DOTALL|re.IGNORECASE), lambda match: match.group().replace('<!--', '').replace('-->', '')), # remove <p> tags from within <a href> tags (re.compile(r'<\s*a\s+[^<>]*href\s*=[^<>]*>(.*?)<\s*/\s*a\s*>', re.DOTALL|re.IGNORECASE), @@ -196,16 +196,16 @@ class HTMLConverter: object.__setattr__(self, attr, val) CSS = { - 'h1' : {"font-size" : "xx-large", "font-weight":"bold", 'text-indent':'0pt'}, - 'h2' : {"font-size" : "x-large", "font-weight":"bold", 'text-indent':'0pt'}, - 'h3' : {"font-size" : "large", "font-weight":"bold", 'text-indent':'0pt'}, - 'h4' : {"font-size" : "large", 'text-indent':'0pt'}, - 'h5' : {"font-weight" : "bold", 'text-indent':'0pt'}, - 'b' : {"font-weight" : "bold"}, - 'strong' : {"font-weight" : "bold"}, - 'i' : {"font-style" : "italic"}, + 'h1' : {'font-size' : 'xx-large', 'font-weight':'bold', 'text-indent':'0pt'}, + 'h2' : {'font-size' : 'x-large', 'font-weight':'bold', 'text-indent':'0pt'}, + 'h3' : {'font-size' : 'large', 'font-weight':'bold', 'text-indent':'0pt'}, + 'h4' : {'font-size' : 'large', 'text-indent':'0pt'}, + 'h5' : {'font-weight' : 'bold', 'text-indent':'0pt'}, + 'b' : {'font-weight' : 'bold'}, + 'strong' : {'font-weight' : 'bold'}, + 'i' : {'font-style' : 'italic'}, 'cite' : {'font-style' : 'italic'}, - 'em' : {"font-style" : "italic"}, + 'em' : {'font-style' : 'italic'}, 'small' : {'font-size' : 'small'}, 'pre' : {'font-family' : 'monospace', 'white-space': 'pre'}, 'code' : {'font-family' : 'monospace'}, @@ -409,12 +409,12 @@ class HTMLConverter: self.processed_files.append(path) def parse_css(self, style): - """ + ''' Parse the contents of a <style> tag or .css file. @param style: C{str(style)} should be the CSS to parse. @return: A dictionary with one entry per selector where the key is the selector name and the value is a dictionary of properties - """ + ''' sdict, pdict = {}, {} style = re.sub(r'/\*.*?\*/', '', style) # Remove /*...*/ comments for sel in re.findall(HTMLConverter.SELECTOR_PAT, style): @@ -440,12 +440,12 @@ class HTMLConverter: return sdict, pdict def parse_style_properties(self, props): - """ + ''' Parses a style attribute. The code within a CSS selector block or in the style attribute of an HTML element. @return: A dictionary with one entry for each property where the key is the property name and the value is the property value. - """ + ''' prop = dict() for s in props.split(';'): l = s.split(':',1) @@ -456,9 +456,9 @@ class HTMLConverter: return prop def tag_css(self, tag, parent_css={}): - """ + ''' Return a dictionary of style properties applicable to Tag tag. - """ + ''' def merge_parent_css(prop, pcss): # float should not be inherited according to the CSS spec # however we need to as we don't do alignment at a block level. @@ -479,29 +479,29 @@ class HTMLConverter: tagname = tag.name.lower() if parent_css: merge_parent_css(prop, parent_css) - if tag.has_attr("align"): + if tag.has_attr('align'): al = tag['align'].lower() if al in ('left', 'right', 'center', 'justify'): - prop["text-align"] = al + prop['text-align'] = al if tagname in self.css: prop.update(self.css[tagname]) if tagname in self.pseudo_css: pprop.update(self.pseudo_css[tagname]) - if tag.has_attr("class"): + if tag.has_attr('class'): cls = tag['class'] if isinstance(cls, list): cls = ' '.join(cls) cls = cls.lower() for cls in cls.split(): - for classname in ["."+cls, tagname+"."+cls]: + for classname in ['.'+cls, tagname+'.'+cls]: if classname in self.css: prop.update(self.css[classname]) if classname in self.pseudo_css: pprop.update(self.pseudo_css[classname]) if tag.has_attr('id') and tag['id'] in self.css: prop.update(self.css[tag['id']]) - if tag.has_attr("style"): - prop.update(self.parse_style_properties(tag["style"])) + if tag.has_attr('style'): + prop.update(self.parse_style_properties(tag['style'])) return prop, pprop def parse_file(self, soup): @@ -614,7 +614,7 @@ class HTMLConverter: hasattr(target.parent, 'objId'): self.book.addTocEntry(ascii_text, tb) else: - self.log.debug("Cannot add link %s to TOC"%ascii_text) + self.log.debug('Cannot add link %s to TOC'%ascii_text) def get_target_block(fragment, targets): '''Return the correct block for the <a name> element''' @@ -689,10 +689,10 @@ class HTMLConverter: self.book.addTocEntry(ascii_text, self.targets[url]) def end_page(self): - """ + ''' End the current page, ensuring that any further content is displayed on a new page. - """ + ''' if self.current_para.has_text(): self.current_para.append_to(self.current_block) self.current_para = Paragraph() @@ -726,7 +726,7 @@ class HTMLConverter: self.book.append(page) def process_children(self, ptag, pcss, ppcss={}): - """ Process the children of ptag """ + ''' Process the children of ptag ''' # Need to make a copy of contents as when # extract is called on a child, it will # mess up the iteration. @@ -748,10 +748,10 @@ class HTMLConverter: val = css['text-align'].lower() if 'text-align' in css else None align = 'head' if val is not None: - if val in ["right", "foot"]: - align = "foot" - elif val == "center": - align = "center" + if val in ['right', 'foot']: + align = 'foot' + elif val == 'center': + align = 'center' if 'float' in css: val = css['float'].lower() if val == 'left': @@ -1152,10 +1152,10 @@ class HTMLConverter: def font_weight(val): ans = 0 - m = re.search("([0-9]+)", val) + m = re.search('([0-9]+)', val) if m: ans = int(m.group(1)) - elif val.find("bold") >= 0 or val.find("strong") >= 0: + elif val.find('bold') >= 0 or val.find('strong') >= 0: ans = 700 return 'bold' if ans >= 700 else 'normal' @@ -1167,10 +1167,10 @@ class HTMLConverter: def font_family(val): ans = 'serif' - if max(val.find("courier"), val.find("mono"), val.find("fixed"), val.find("typewriter"))>=0: + if max(val.find('courier'), val.find('mono'), val.find('fixed'), val.find('typewriter'))>=0: ans = 'mono' - elif max(val.find("arial"), val.find("helvetica"), val.find("verdana"), - val.find("trebuchet"), val.find("sans")) >= 0: + elif max(val.find('arial'), val.find('helvetica'), val.find('verdana'), + val.find('trebuchet'), val.find('sans')) >= 0: ans = 'sans' return ans @@ -1201,29 +1201,29 @@ class HTMLConverter: if ans <= 0: ans += normal if ans == 0: # Common case of using -1em to mean "smaller" - ans = int(font_size("smaller")) + ans = int(font_size('smaller')) if ans < 0: ans = normal else: if ans == 0: - ans = int(font_size("smaller")) - elif "smaller" in val: + ans = int(font_size('smaller')) + elif 'smaller' in val: ans = normal - 20 - elif "xx-small" in val: + elif 'xx-small' in val: ans = 40 - elif "x-small" in val: + elif 'x-small' in val: ans = 60 - elif "small" in val: + elif 'small' in val: ans = 80 - elif "medium" in val: + elif 'medium' in val: ans = 100 - elif "larger" in val: + elif 'larger' in val: ans = normal + 20 - elif "xx-large" in val: + elif 'xx-large' in val: ans = 180 - elif "x-large" in val: + elif 'x-large' in val: ans = 140 - elif "large" in val: + elif 'large' in val: ans = 120 if ans is not None: ans += int(self.font_delta * 20) @@ -1259,7 +1259,7 @@ class HTMLConverter: break elif key in ['font-family', 'font-name']: family = font_family(val) - elif key == "font-size": + elif key == 'font-size': ans = font_size(val) if ans: t['fontsize'] = ans @@ -1299,7 +1299,7 @@ class HTMLConverter: result = int(val) except ValueError: pass - m = re.search(r"\s*(-*[0-9]*\.?[0-9]*)\s*(%|em|px|mm|cm|in|dpt|pt|pc)", val) + m = re.search(r'\s*(-*[0-9]*\.?[0-9]*)\s*(%|em|px|mm|cm|in|dpt|pt|pc)', val) if m is not None and m.group(1): unit = float(m.group(1)) @@ -1481,7 +1481,7 @@ class HTMLConverter: end_page = self.process_page_breaks(tag, tagname, tag_css) try: - if tagname in ["title", "script", "meta", 'del', 'frameset']: + if tagname in ['title', 'script', 'meta', 'del', 'frameset']: pass elif tagname == 'a' and self.link_levels >= 0: if tag.has_attr('href') and not self.link_exclude.match(tag['href']): @@ -1498,7 +1498,7 @@ class HTMLConverter: else: text = self.get_text(tag, limit=1000) if not text.strip(): - text = "Link" + text = 'Link' self.add_text(text, tag_css, {}, force_span_use=True) self.links.append(self.create_link(self.current_para.contents, tag)) if tag.has_attr('id') or tag.has_attr('name'): @@ -1530,7 +1530,7 @@ class HTMLConverter: elif not urlparse(tag['src'])[0]: self.log.warn('Could not find image: '+tag['src']) else: - self.log.debug("Failed to process: %s"%str(tag)) + self.log.debug('Failed to process: %s'%str(tag)) elif tagname in ['style', 'link']: ncss, npcss = {}, {} if tagname == 'style': @@ -1538,7 +1538,7 @@ class HTMLConverter: css, pcss = self.parse_css(text) ncss.update(css) npcss.update(pcss) - elif (tag.has_attr('type') and tag['type'] in ("text/css", "text/x-oeb1-css") and tag.has_attr('href')): + elif (tag.has_attr('type') and tag['type'] in ('text/css', 'text/x-oeb1-css') and tag.has_attr('href')): path = munge_paths(self.target_prefix, tag['href'])[0] try: with open(path, 'rb') as f: @@ -1815,13 +1815,13 @@ def process_file(path, options, logger): try: cim = im.resize((width, height), PILImage.BICUBIC).convert('RGB') if \ scaled else im - cf = PersistentTemporaryFile(prefix=__appname__+"_", suffix=".jpg") + cf = PersistentTemporaryFile(prefix=__appname__+'_', suffix='.jpg') cf.close() cim.convert('RGB').save(cf.name) options.cover = cf.name tim = im.resize((int(0.75*th), th), PILImage.Resampling.LANCZOS).convert('RGB') - tf = PersistentTemporaryFile(prefix=__appname__+'_', suffix=".jpg") + tf = PersistentTemporaryFile(prefix=__appname__+'_', suffix='.jpg') tf.close() tim.save(tf.name) tpath = tf.name @@ -1861,12 +1861,12 @@ def process_file(path, options, logger): if not options.author: options.author = _('Unknown') if not fheader: - fheader = "%t by %a" + fheader = '%t by %a' fheader = re.sub(r'(?<!%)%t', options.title, fheader) fheader = re.sub(r'(?<!%)%a', options.author, fheader) fheader = re.sub(r'%%a','%a',fheader) fheader = re.sub(r'%%t','%t',fheader) - header.append(fheader + " ") + header.append(fheader + ' ') book, fonts = Book(options, logger, header=header, **args) le = re.compile(options.link_exclude) if options.link_exclude else \ re.compile('$') diff --git a/src/calibre/ebooks/lrf/html/table.py b/src/calibre/ebooks/lrf/html/table.py index 2568893722..803873ed30 100644 --- a/src/calibre/ebooks/lrf/html/table.py +++ b/src/calibre/ebooks/lrf/html/table.py @@ -145,7 +145,7 @@ class Cell: if not token.strip(): continue word = token.split() - word = word[0] if word else "" + word = word[0] if word else '' fl, ft, fr, fb = font.getbbox(word) width = fr - fl if width > mwidth: diff --git a/src/calibre/ebooks/lrf/input.py b/src/calibre/ebooks/lrf/input.py index 5d46a201be..1d64e14f08 100644 --- a/src/calibre/ebooks/lrf/input.py +++ b/src/calibre/ebooks/lrf/input.py @@ -66,8 +66,8 @@ class Canvas(etree.XSLTExtension): div = etree.Element('div') div.set('id', input_node.get('objid', 'scuzzy')) div.set('class', 'image_page') - width = self.styles.to_num(block.get("xsize", None)) - height = self.styles.to_num(block.get("ysize", None)) + width = self.styles.to_num(block.get('xsize', None)) + height = self.styles.to_num(block.get('ysize', None)) img = div.makeelement('img') if width is not None: img.set('width', str(int(width))) @@ -197,7 +197,7 @@ class TextBlock(etree.XSLTExtension): pattrib = dict(**p.attrib) if p.tag == 'Span' else {} for child in children: p.remove(child) - if pattrib and child.tag == "Span": + if pattrib and child.tag == 'Span': attrib = copy(pattrib) attrib.update(child.attrib) child.attrib.update(attrib) diff --git a/src/calibre/ebooks/lrf/lrfparser.py b/src/calibre/ebooks/lrf/lrfparser.py index 44bf1f80f3..59837c1718 100644 --- a/src/calibre/ebooks/lrf/lrfparser.py +++ b/src/calibre/ebooks/lrf/lrfparser.py @@ -49,8 +49,8 @@ class LRFDocument(LRFMetaFile): def _parse_objects(self): self.objects = {} self._file.seek(self.object_index_offset) - obj_array = array.array("I", self._file.read(4*4*self.number_of_objects)) - if ord(array.array("i",[1]).tobytes()[0:1])==0: # big-endian + obj_array = array.array('I', self._file.read(4*4*self.number_of_objects)) + if ord(array.array('i',[1]).tobytes()[0:1])==0: # big-endian obj_array.byteswap() for i in range(self.number_of_objects): if not self.keep_parsing: @@ -163,7 +163,7 @@ def main(args=sys.argv, logger=None): parser.print_help() return 1 if opts.out is None: - opts.out = os.path.join(os.path.dirname(args[1]), os.path.splitext(os.path.basename(args[1]))[0]+".lrs") + opts.out = os.path.join(os.path.dirname(args[1]), os.path.splitext(os.path.basename(args[1]))[0]+'.lrs') logger.info(_('Parsing LRF...')) d = LRFDocument(open(args[1], 'rb')) d.parse() diff --git a/src/calibre/ebooks/lrf/meta.py b/src/calibre/ebooks/lrf/meta.py index 7893c66fb7..789b293930 100644 --- a/src/calibre/ebooks/lrf/meta.py +++ b/src/calibre/ebooks/lrf/meta.py @@ -1,7 +1,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' -""" +''' This module presents an easy to use interface for getting and setting meta information in LRF files. Just create an L{LRFMetaFile} object and use its properties @@ -10,7 +10,7 @@ to get and set meta information. For example: >>> lrf = LRFMetaFile("mybook.lrf") >>> print(lrf.title, lrf.author) >>> lrf.category = "History" -""" +''' import io import os @@ -26,23 +26,23 @@ from calibre.ebooks.metadata import MetaInformation, string_to_authors from calibre.utils.cleantext import clean_xml_chars from polyglot.builtins import string_or_bytes -BYTE = "<B" #: Unsigned char little endian encoded in 1 byte -WORD = "<H" #: Unsigned short little endian encoded in 2 bytes -DWORD = "<I" #: Unsigned integer little endian encoded in 4 bytes -QWORD = "<Q" #: Unsigned long long little endian encoded in 8 bytes +BYTE = '<B' #: Unsigned char little endian encoded in 1 byte +WORD = '<H' #: Unsigned short little endian encoded in 2 bytes +DWORD = '<I' #: Unsigned integer little endian encoded in 4 bytes +QWORD = '<Q' #: Unsigned long long little endian encoded in 8 bytes class field: - """ A U{Descriptor<http://www.cafepy.com/article/python_attributes_and_methods/python_attributes_and_methods.html>}, that implements access + ''' A U{Descriptor<http://www.cafepy.com/article/python_attributes_and_methods/python_attributes_and_methods.html>}, that implements access to protocol packets in a human readable way. - """ + ''' def __init__(self, start=16, fmt=DWORD): - """ + ''' @param start: The byte at which this field is stored in the buffer @param fmt: The packing format for this field. See U{struct<http://docs.python.org/lib/module-struct.html>}. - """ + ''' self._fmt, self._start = fmt, start def __get__(self, obj, typ=None): @@ -53,9 +53,9 @@ class field: def __repr__(self): typ = {DWORD: 'unsigned int', 'QWORD': 'unsigned long long', BYTE: 'unsigned char', WORD: 'unsigned short'}.get(self._fmt, '') - return "An " + typ + " stored in " + \ + return 'An ' + typ + ' stored in ' + \ str(struct.calcsize(self._fmt)) + \ - " bytes starting at byte " + str(self._start) + ' bytes starting at byte ' + str(self._start) class versioned_field(field): @@ -75,7 +75,7 @@ class versioned_field(field): def __set__(self, obj, val): if not self.enabled(obj): - raise LRFException("Trying to set disabled field") + raise LRFException('Trying to set disabled field') else: field.__set__(self, obj, val) @@ -85,19 +85,19 @@ class LRFException(Exception): class fixed_stringfield: - """ A field storing a variable length string. """ + ''' A field storing a variable length string. ''' def __init__(self, length=8, start=0): - """ + ''' @param length: Size of this string @param start: The byte at which this field is stored in the buffer - """ + ''' self._length = length self._start = start def __get__(self, obj, typ=None): length = str(self._length) - return obj.unpack(start=self._start, fmt="<"+length+"s")[0] + return obj.unpack(start=self._start, fmt='<'+length+'s')[0] def __set__(self, obj, val): if not isinstance(val, string_or_bytes): @@ -105,13 +105,13 @@ class fixed_stringfield: if isinstance(val, str): val = val.encode('utf-8') if len(val) != self._length: - raise LRFException("Trying to set fixed_stringfield with a " + - "string of incorrect length") - obj.pack(val, start=self._start, fmt="<"+str(len(val))+"s") + raise LRFException('Trying to set fixed_stringfield with a ' + + 'string of incorrect length') + obj.pack(val, start=self._start, fmt='<'+str(len(val))+'s') def __repr__(self): - return "A string of length " + str(self._length) + \ - " starting at byte " + str(self._start) + return 'A string of length ' + str(self._length) + \ + ' starting at byte ' + str(self._start) class xml_attr_field: @@ -136,7 +136,7 @@ class xml_attr_field: def __set__(self, obj, val): if val is None: - val = "" + val = '' document = obj.info elems = document.getElementsByTagName(self.tag_name) if len(elems): @@ -149,23 +149,23 @@ class xml_attr_field: obj.info = document def __repr__(self): - return "XML Attr Field: " + self.tag_name + " in " + self.parent + return 'XML Attr Field: ' + self.tag_name + ' in ' + self.parent def __str__(self): return self.tag_name+'.'+self.attr class xml_field: - """ + ''' Descriptor that gets and sets XML based meta information from an LRF file. Works for simple XML fields of the form <tagname>data</tagname> - """ + ''' - def __init__(self, tag_name, parent="BookInfo"): - """ + def __init__(self, tag_name, parent='BookInfo'): + ''' @param tag_name: The XML tag whose data we operate on @param parent: The tagname of the parent element of C{tag_name} - """ + ''' self.tag_name = tag_name self.parent = parent @@ -223,11 +223,11 @@ class xml_field: return self.tag_name def __repr__(self): - return "XML Field: " + self.tag_name + " in " + self.parent + return 'XML Field: ' + self.tag_name + ' in ' + self.parent def insert_into_file(fileobj, data, start, end): - """ + ''' Insert data into fileobj at position C{start}. This function inserts data into a file, overwriting all data between start @@ -239,7 +239,7 @@ def insert_into_file(fileobj, data, start, end): @param start: The position at which to start inserting data @param end: The position in fileobj of data that must not be overwritten @return: C{start + len(data) - end} - """ + ''' buffer = io.BytesIO() fileobj.seek(end) copyfileobj(fileobj, buffer, -1) @@ -257,11 +257,11 @@ def insert_into_file(fileobj, data, start, end): def get_metadata(stream): - """ + ''' Return basic meta-data about the LRF file in C{stream} as a L{MetaInformation} object. @param stream: A file like object or an instance of L{LRFMetaFile} - """ + ''' lrf = stream if isinstance(stream, LRFMetaFile) else LRFMetaFile(stream) authors = string_to_authors(lrf.author) mi = MetaInformation(lrf.title.strip(), authors) @@ -303,7 +303,7 @@ def get_metadata(stream): class LRFMetaFile: - """ Has properties to read and write all Meta information in a LRF file. """ + ''' Has properties to read and write all Meta information in a LRF file. ''' #: The first 6 bytes of all valid LRF files LRF_HEADER = 'LRF'.encode('utf-16le') @@ -326,30 +326,30 @@ class LRFMetaFile: uncompressed_info_size = versioned_field(compressed_info_size, 0, fmt=DWORD, start=0x54) - title = xml_field("Title", parent="BookInfo") - title_reading = xml_attr_field("Title", 'reading', parent="BookInfo") - author = xml_field("Author", parent="BookInfo") - author_reading = xml_attr_field("Author", 'reading', parent="BookInfo") + title = xml_field('Title', parent='BookInfo') + title_reading = xml_attr_field('Title', 'reading', parent='BookInfo') + author = xml_field('Author', parent='BookInfo') + author_reading = xml_attr_field('Author', 'reading', parent='BookInfo') # 16 characters. First two chars should be FB for personal use ebooks. - book_id = xml_field("BookID", parent="BookInfo") - publisher = xml_field("Publisher", parent="BookInfo") - label = xml_field("Label", parent="BookInfo") - category = xml_field("Category", parent="BookInfo") - classification = xml_field("Classification", parent="BookInfo") - free_text = xml_field("FreeText", parent="BookInfo") + book_id = xml_field('BookID', parent='BookInfo') + publisher = xml_field('Publisher', parent='BookInfo') + label = xml_field('Label', parent='BookInfo') + category = xml_field('Category', parent='BookInfo') + classification = xml_field('Classification', parent='BookInfo') + free_text = xml_field('FreeText', parent='BookInfo') # Should use ISO 639 language codes - language = xml_field("Language", parent="DocInfo") - creator = xml_field("Creator", parent="DocInfo") + language = xml_field('Language', parent='DocInfo') + creator = xml_field('Creator', parent='DocInfo') # Format is %Y-%m-%d - creation_date = xml_field("CreationDate", parent="DocInfo") - producer = xml_field("Producer", parent="DocInfo") - page = xml_field("SumPage", parent="DocInfo") + creation_date = xml_field('CreationDate', parent='DocInfo') + producer = xml_field('Producer', parent='DocInfo') + page = xml_field('SumPage', parent='DocInfo') def safe(func): - """ + ''' Decorator that ensures that function calls leave the pos in the underlying file unchanged - """ + ''' @wraps(func) def restore_pos(*args, **kwargs): obj = args[0] @@ -362,10 +362,10 @@ class LRFMetaFile: return restore_pos def safe_property(func): - """ + ''' Decorator that ensures that read or writing a property leaves the position in the underlying file unchanged - """ + ''' def decorator(f): def restore_pos(*args, **kwargs): obj = args[0] @@ -378,34 +378,34 @@ class LRFMetaFile: return restore_pos locals_ = func() if 'fget' in locals_: - locals_["fget"] = decorator(locals_["fget"]) + locals_['fget'] = decorator(locals_['fget']) if 'fset' in locals_: - locals_["fset"] = decorator(locals_["fset"]) + locals_['fset'] = decorator(locals_['fset']) return property(**locals_) @safe_property def info(): doc = \ - """ + ''' Document meta information as a minidom Document object. To set use a minidom document object. - """ + ''' def fget(self): if self.compressed_info_size == 0: - raise LRFException("This document has no meta info") + raise LRFException('This document has no meta info') size = self.compressed_info_size - 4 self._file.seek(self.info_start) try: src = zlib.decompress(self._file.read(size)) if len(src) != self.uncompressed_info_size: - raise LRFException("Decompression of document meta info\ - yielded unexpected results") + raise LRFException('Decompression of document meta info\ + yielded unexpected results') src = xml_to_unicode(src, strip_encoding_pats=True, resolve_entities=True, assume_utf8=True)[0] return dom.parseString(clean_xml_chars(src)) except zlib.error: - raise LRFException("Unable to decompress document meta information") + raise LRFException('Unable to decompress document meta information') def fset(self, document): info = document.toxml('utf-8') @@ -421,36 +421,36 @@ class LRFMetaFile: self.object_index_offset += delta self.update_object_offsets(delta) - return {"fget":fget, "fset":fset, "doc":doc} + return {'fget':fget, 'fset':fset, 'doc':doc} @safe_property def thumbnail_pos(): - doc = """ The position of the thumbnail in the LRF file """ + doc = ''' The position of the thumbnail in the LRF file ''' def fget(self): return self.info_start + self.compressed_info_size-4 - return {"fget":fget, "doc":doc} + return {'fget':fget, 'doc':doc} @classmethod def _detect_thumbnail_type(cls, slice): - """ @param slice: The first 16 bytes of the thumbnail """ + ''' @param slice: The first 16 bytes of the thumbnail ''' ttype = 0x14 # GIF - if "PNG" in slice: + if 'PNG' in slice: ttype = 0x12 - if "BM" in slice: + if 'BM' in slice: ttype = 0x13 - if "JFIF" in slice: + if 'JFIF' in slice: ttype = 0x11 return ttype @safe_property def thumbnail(): doc = \ - """ + ''' The thumbnail. Represented as a string. The string you would get from the file read function. - """ + ''' def fget(self): size = self.thumbnail_size @@ -460,8 +460,8 @@ class LRFMetaFile: def fset(self, data): if self.version <= 800: - raise LRFException("Cannot store thumbnails in LRF files \ - of version <= 800") + raise LRFException('Cannot store thumbnails in LRF files \ + of version <= 800') slice = data[0:16] orig_size = self.thumbnail_size self.thumbnail_size = len(data) @@ -472,22 +472,22 @@ class LRFMetaFile: self.thumbnail_type = self._detect_thumbnail_type(slice) self.update_object_offsets(delta) - return {"fget":fget, "fset":fset, "doc":doc} + return {'fget':fget, 'fset':fset, 'doc':doc} def __init__(self, file): - """ @param file: A file object opened in the r+b mode """ + ''' @param file: A file object opened in the r+b mode ''' file.seek(0, 2) self.size = file.tell() self._file = file if self.lrf_header != LRFMetaFile.LRF_HEADER: raise LRFException(file.name + - " has an invalid LRF header. Are you sure it is an LRF file?") + ' has an invalid LRF header. Are you sure it is an LRF file?') # Byte at which the compressed meta information starts self.info_start = 0x58 if self.version > 800 else 0x53 @safe def update_object_offsets(self, delta): - """ Run through the LRF Object index changing the offset by C{delta}. """ + ''' Run through the LRF Object index changing the offset by C{delta}. ''' self._file.seek(self.object_index_offset) count = self.number_of_objects while count > 0: @@ -503,12 +503,12 @@ class LRFMetaFile: @safe def unpack(self, fmt=DWORD, start=0): - """ + ''' Return decoded data from file. @param fmt: See U{struct<http://docs.python.org/lib/module-struct.html>} @param start: Position in file from which to decode - """ + ''' end = start + struct.calcsize(fmt) self._file.seek(start) ret = struct.unpack(fmt, self._file.read(end-start)) @@ -516,57 +516,57 @@ class LRFMetaFile: @safe def pack(self, *args, **kwargs): - """ + ''' Encode C{args} and write them to file. C{kwargs} must contain the keywords C{fmt} and C{start} @param args: The values to pack @param fmt: See U{struct<http://docs.python.org/lib/module-struct.html>} @param start: Position in file at which to write encoded data - """ - encoded = struct.pack(kwargs["fmt"], *args) - self._file.seek(kwargs["start"]) + ''' + encoded = struct.pack(kwargs['fmt'], *args) + self._file.seek(kwargs['start']) self._file.write(encoded) self._file.flush() def thumbail_extension(self): - """ + ''' Return the extension for the thumbnail image type as specified by L{self.thumbnail_type}. If the LRF file was created by buggy software, the extension maye be incorrect. See L{self.fix_thumbnail_type}. - """ - ext = "gif" + ''' + ext = 'gif' ttype = self.thumbnail_type if ttype == 0x11: - ext = "jpeg" + ext = 'jpeg' elif ttype == 0x12: - ext = "png" + ext = 'png' elif ttype == 0x13: - ext = "bmp" + ext = 'bmp' return ext def fix_thumbnail_type(self): - """ + ''' Attempt to guess the thumbnail image format and set L{self.thumbnail_type} accordingly. - """ + ''' slice = self.thumbnail[0:16] self.thumbnail_type = self._detect_thumbnail_type(slice) def seek(self, *args): - """ See L{file.seek} """ + ''' See L{file.seek} ''' return self._file.seek(*args) def tell(self): - """ See L{file.tell} """ + ''' See L{file.tell} ''' return self._file.tell() def read(self): - """ See L{file.read} """ + ''' See L{file.read} ''' return self._file.read() def write(self, val): - """ See L{file.write} """ + ''' See L{file.write} ''' self._file.write(val) def _objects(self): @@ -586,7 +586,7 @@ class LRFMetaFile: self._file.seek(offset) tag = Tag(self._file) if tag.id == 0xF500: - obj_id, obj_type = struct.unpack("<IH", tag.contents) + obj_id, obj_type = struct.unpack('<IH', tag.contents) if obj_type == type: objects.append((obj_id, offset, size)) return objects @@ -597,7 +597,7 @@ class LRFMetaFile: self._file.seek(offset) tag = Tag(self._file) if tag.id == 0xF500: - obj_id, obj_type = struct.unpack("<IH", tag.contents) + obj_id, obj_type = struct.unpack('<IH', tag.contents) if obj_id == tid: return obj_id, offset, size, obj_type return (False, False, False, False) @@ -623,26 +623,26 @@ def option_parser(): Show/edit the metadata in an LRF file.\n\n'''), version=__appname__+' '+__version__, epilog='Created by Kovid Goyal') - parser.add_option("-t", "--title", action="store", type="string", - dest="title", help=_("Set the book title")) + parser.add_option('-t', '--title', action='store', type='string', + dest='title', help=_('Set the book title')) parser.add_option('--title-sort', action='store', type='string', default=None, dest='title_reading', help=_('Set sort key for the title')) - parser.add_option("-a", "--author", action="store", type="string", - dest="author", help=_("Set the author")) + parser.add_option('-a', '--author', action='store', type='string', + dest='author', help=_('Set the author')) parser.add_option('--author-sort', action='store', type='string', default=None, dest='author_reading', help=_('Set sort key for the author')) - parser.add_option("-c", "--category", action="store", type="string", - dest="category", help=_("The category this book belongs" - " to. E.g.: History")) - parser.add_option("--thumbnail", action="store", type="string", - dest="thumbnail", help=_("Path to a graphic that will be" + parser.add_option('-c', '--category', action='store', type='string', + dest='category', help=_('The category this book belongs' + ' to. E.g.: History')) + parser.add_option('--thumbnail', action='store', type='string', + dest='thumbnail', help=_("Path to a graphic that will be" " set as this files' thumbnail")) - parser.add_option("--comment", action="store", type="string", - dest="comment", help=_("Path to a TXT file containing the " - "comment to be stored in the LRF file.")) - parser.add_option("--get-thumbnail", action="store_true", - dest="get_thumbnail", default=False, - help=_("Extract thumbnail from LRF file")) + parser.add_option('--comment', action='store', type='string', + dest='comment', help=_('Path to a TXT file containing the ' + 'comment to be stored in the LRF file.')) + parser.add_option('--get-thumbnail', action='store_true', + dest='get_thumbnail', default=False, + help=_('Extract thumbnail from LRF file')) parser.add_option('--publisher', default=None, help=_('Set the publisher')) parser.add_option('--classification', default=None, help=_('Set the book classification')) parser.add_option('--creator', default=None, help=_('Set the book creator')) @@ -687,7 +687,7 @@ def main(args=sys.argv): print() print('No lrf file specified') return 1 - lrf = LRFMetaFile(open(args[1], "r+b")) + lrf = LRFMetaFile(open(args[1], 'r+b')) if options.title: lrf.title = options.title @@ -709,7 +709,7 @@ def main(args=sys.argv): lrf.producer = options.producer if options.thumbnail: path = os.path.expanduser(os.path.expandvars(options.thumbnail)) - with open(path, "rb") as f: + with open(path, 'rb') as f: lrf.thumbnail = f.read() if options.book_id is not None: lrf.book_id = options.book_id @@ -719,26 +719,26 @@ def main(args=sys.argv): lrf.free_text = f.read().decode('utf-8', 'replace') if options.get_thumbnail: t = lrf.thumbnail - td = "None" + td = 'None' if t and len(t) > 0: - td = os.path.basename(args[1])+"_thumbnail."+lrf.thumbail_extension() - with open(td, "wb") as f: + td = os.path.basename(args[1])+'_thumbnail.'+lrf.thumbail_extension() + with open(td, 'wb') as f: f.write(t) fields = LRFMetaFile.__dict__.items() fields.sort() for f in fields: - if "XML" in str(f): - print(str(f[1]) + ":", lrf.__getattribute__(f[0]).encode('utf-8')) + if 'XML' in str(f): + print(str(f[1]) + ':', lrf.__getattribute__(f[0]).encode('utf-8')) if options.get_thumbnail: - print("Thumbnail:", td) + print('Thumbnail:', td) if options.get_cover: try: ext, data = lrf.get_cover() except: # Fails on books created by LRFCreator 1.0 ext, data = None, None if data: - cover = os.path.splitext(os.path.basename(args[1]))[0]+"_cover."+ext + cover = os.path.splitext(os.path.basename(args[1]))[0]+'_cover.'+ext with open(cover, 'wb') as f: f.write(data) print('Cover:', cover) diff --git a/src/calibre/ebooks/lrf/objects.py b/src/calibre/ebooks/lrf/objects.py index 9f9f0d92c6..d20805c349 100644 --- a/src/calibre/ebooks/lrf/objects.py +++ b/src/calibre/ebooks/lrf/objects.py @@ -75,7 +75,7 @@ class LRFObject: self.handle_tag(tag, stream) def parse_bg_image(self, tag, f): - self.bg_image_mode, self.bg_image_id = struct.unpack("<HI", tag.contents) + self.bg_image_mode, self.bg_image_id = struct.unpack('<HI', tag.contents) def handle_tag(self, tag, stream, tag_map=None): if tag_map is None: @@ -86,7 +86,7 @@ class LRFObject: if h[1] != '' and h[0] != '': setattr(self, h[0], val) else: - raise LRFParseError(f"Unknown tag in {self.__class__.__name__}: {str(tag)}") + raise LRFParseError(f'Unknown tag in {self.__class__.__name__}: {str(tag)}') def __iter__(self): yield from range(0) @@ -129,7 +129,7 @@ class LRFContentObject(LRFObject): func, args = action[0], (action[1],) getattr(self, func)(tag, *args) else: - raise LRFParseError(f"Unknown tag in {self.__class__.__name__}: {str(tag)}") + raise LRFParseError(f'Unknown tag in {self.__class__.__name__}: {str(tag)}') def __iter__(self): yield from self._contents @@ -173,12 +173,12 @@ class LRFStream(LRFObject): l = 0x400 self.stream = self.descramble_buffer(self.stream, l, key) if self.stream_flags & 0x100 !=0: - decomp_size = struct.unpack("<I", self.stream[:4])[0] + decomp_size = struct.unpack('<I', self.stream[:4])[0] self.stream = zlib.decompress(self.stream[4:]) if len(self.stream) != decomp_size: - raise LRFParseError("Stream decompressed size is wrong!") + raise LRFParseError('Stream decompressed size is wrong!') if stream.read(2) != b'\x06\xF5': - print("Warning: corrupted end-of-stream tag at %08X; skipping it"%(stream.tell()-2)) + print('Warning: corrupted end-of-stream tag at %08X; skipping it'%(stream.tell()-2)) self.end_stream(None, None) @@ -237,7 +237,7 @@ class PageAttr(StyleObject, LRFObject): 0xF52B: ['pageposition', 'W', {0: 'any', 1:'upper', 2: 'lower'}], 0xF52A: ['setemptyview', 'W', {1: 'show', 0: 'empty'}], 0xF5DA: ['setwaitprop', 'W', {1: 'replay', 2: 'noreplay'}], - 0xF529: ['', "parse_bg_image"], + 0xF529: ['', 'parse_bg_image'], } tag_map.update(LRFObject.tag_map) @@ -366,7 +366,7 @@ class Page(LRFStream): def page_div(self, tag): self.close_blockspace() - pars = struct.unpack("<HIHI", tag.contents) + pars = struct.unpack('<HIHI', tag.contents) self._contents.append(PageDiv(*pars)) def x_space(self, tag): @@ -383,7 +383,7 @@ class Page(LRFStream): def ruled_line(self, tag): self.close_blockspace() - pars = struct.unpack("<HHHI", tag.contents) + pars = struct.unpack('<HHHI', tag.contents) self._contents.append(RuledLine(*pars)) def wait(self, tag): @@ -440,12 +440,12 @@ class BlockAttr(StyleObject, LRFObject): 0xF531: ['blockwidth', 'W'], 0xF532: ['blockheight', 'W'], 0xF533: ['blockrule', 'W', { - 0x14: "horz-fixed", - 0x12: "horz-adjustable", - 0x41: "vert-fixed", - 0x21: "vert-adjustable", - 0x44: "block-fixed", - 0x22: "block-adjustable"}], + 0x14: 'horz-fixed', + 0x12: 'horz-adjustable', + 0x41: 'vert-fixed', + 0x21: 'vert-adjustable', + 0x44: 'block-fixed', + 0x22: 'block-adjustable'}], 0xF534: ['bgcolor', 'D', Color], 0xF535: ['layout', 'W', {0x41: 'TbRl', 0x34: 'LrTb'}], 0xF536: ['framewidth', 'W'], @@ -579,7 +579,7 @@ class Block(LRFStream, TextCSS): stream = io.BytesIO(self.stream) tag = Tag(stream) if tag.id != 0xF503: - raise LRFParseError("Bad block content") + raise LRFParseError('Bad block content') obj = self._document.objects[tag.dword] if isinstance(obj, SimpleText): self.name = 'SimpleTextBlock' @@ -595,7 +595,7 @@ class Block(LRFStream, TextCSS): elif isinstance(obj, Button): self.name = 'ButtonBlock' else: - raise LRFParseError("Unexpected block type: "+obj.__class__.__name__) + raise LRFParseError('Unexpected block type: '+obj.__class__.__name__) self.content = obj @@ -640,7 +640,7 @@ class Text(LRFStream): style = property(fget=lambda self : self._document.objects[self.style_id]) - text_map = {0x22: '"', 0x26: '&', 0x27: '\'', 0x3c: '<', 0x3e: '>'} + text_map = {0x22: '"', 0x26: '&', 0x27: "'", 0x3c: '<', 0x3e: '>'} entity_pattern = re.compile(r'&(\S+?);') text_tags = { @@ -709,7 +709,7 @@ class Text(LRFStream): lineposition_map = {1:'before', 2:'after'} def add_text(self, text): - s = str(text, "utf-16-le") + s = str(text, 'utf-16-le') if s: s = s.translate(self.text_map) self.content.append(self.entity_pattern.sub(entity_to_unicode_in_python, s)) @@ -789,7 +789,7 @@ class Text(LRFStream): self_closing=True)) def plot(self, tag, stream): - xsize, ysize, refobj, adjustment = struct.unpack("<HHII", tag.contents) + xsize, ysize, refobj, adjustment = struct.unpack('<HHII', tag.contents) plot = self.__class__.TextTag('Plot', {'xsize': xsize, 'ysize': ysize, 'refobj':refobj, 'adjustment':self.adjustment_map[adjustment]}, self_closing=True) @@ -926,10 +926,10 @@ class Image(LRFObject): } def parse_image_rect(self, tag, f): - self.x0, self.y0, self.x1, self.y1 = struct.unpack("<HHHH", tag.contents) + self.x0, self.y0, self.x1, self.y1 = struct.unpack('<HHHH', tag.contents) def parse_image_size(self, tag, f): - self.xsize, self.ysize = struct.unpack("<HH", tag.contents) + self.xsize, self.ysize = struct.unpack('<HH', tag.contents) encoding = property(fget=lambda self : self._document.objects[self.refstream].encoding) data = property(fget=lambda self : self._document.objects[self.refstream].stream) @@ -954,7 +954,7 @@ class Canvas(LRFStream): 0xF551: ['canvaswidth', 'W'], 0xF552: ['canvasheight', 'W'], 0xF5DA: ['', 'parse_waits'], - 0xF533: ['blockrule', 'W', {0x44: "block-fixed", 0x22: "block-adjustable"}], + 0xF533: ['blockrule', 'W', {0x44: 'block-fixed', 0x22: 'block-adjustable'}], 0xF534: ['bgcolor', 'D', Color], 0xF535: ['layout', 'W', {0x41: 'TbRl', 0x34: 'LrTb'}], 0xF536: ['framewidth', 'W'], @@ -983,7 +983,7 @@ class Canvas(LRFStream): try: self._contents.append( PutObj(self._document.objects, - *struct.unpack("<HHI", tag.contents))) + *struct.unpack('<HHI', tag.contents))) except struct.error: print('Canvas object has errors, skipping.') @@ -1087,7 +1087,7 @@ class Button(LRFObject): self.actions[self.button_type] = [] def parse_jump_to(self, tag, f): - self.actions[self.button_type].append((1, struct.unpack("<II", tag.contents))) + self.actions[self.button_type].append((1, struct.unpack('<II', tag.contents))) def parse_send_message(self, tag, f): params = (tag.word, Tag.string_parser(f), Tag.string_parser(f)) @@ -1100,7 +1100,7 @@ class Button(LRFObject): self.actions[self.button_type].append((4,)) def parse_run(self, tag, f): - self.actions[self.button_type].append((5, struct.unpack("<HI", tag.contents))) + self.actions[self.button_type].append((5, struct.unpack('<HI', tag.contents))) def jump_action(self, button_type): for i in self.actions[button_type]: @@ -1211,13 +1211,13 @@ class TOCObject(LRFStream): def initialize(self): stream = io.BytesIO(self.stream or b'') - c = struct.unpack("<H", stream.read(2))[0] + c = struct.unpack('<H', stream.read(2))[0] stream.seek(4*(c+1)) self._contents = [] while c > 0: - refpage = struct.unpack("<I", stream.read(4))[0] - refobj = struct.unpack("<I", stream.read(4))[0] - cnt = struct.unpack("<H", stream.read(2))[0] + refpage = struct.unpack('<I', stream.read(4))[0] + refobj = struct.unpack('<I', stream.read(4))[0] + cnt = struct.unpack('<H', stream.read(2))[0] raw = stream.read(cnt) label = raw.decode('utf_16_le') self._contents.append(TocLabel(refpage, refobj, label)) @@ -1273,8 +1273,8 @@ def get_object(document, stream, id, offset, size, scramble_key): start_tag = Tag(stream) if start_tag.id != 0xF500: raise LRFParseError('Bad object start') - obj_id, obj_type = struct.unpack("<IH", start_tag.contents) + obj_id, obj_type = struct.unpack('<IH', start_tag.contents) if obj_type < len(object_map) and object_map[obj_type] is not None: return object_map[obj_type](document, stream, obj_id, scramble_key, offset+size-Tag.tags[0][0]) - raise LRFParseError("Unknown object type: %02X!" % obj_type) + raise LRFParseError('Unknown object type: %02X!' % obj_type) diff --git a/src/calibre/ebooks/lrf/pylrs/__init__.py b/src/calibre/ebooks/lrf/pylrs/__init__.py index f4d0439228..70ea7dc0dc 100644 --- a/src/calibre/ebooks/lrf/pylrs/__init__.py +++ b/src/calibre/ebooks/lrf/pylrs/__init__.py @@ -1,5 +1,5 @@ -""" +''' This package contains code to generate ebooks in the SONY LRS/F format. It was originally developed by Mike Higgins and has been extended and modified by Kovid Goyal. -""" +''' diff --git a/src/calibre/ebooks/lrf/pylrs/elements.py b/src/calibre/ebooks/lrf/pylrs/elements.py index 79ee5ba864..c5326ea043 100644 --- a/src/calibre/ebooks/lrf/pylrs/elements.py +++ b/src/calibre/ebooks/lrf/pylrs/elements.py @@ -1,12 +1,12 @@ -""" elements.py -- replacements and helpers for ElementTree """ +''' elements.py -- replacements and helpers for ElementTree ''' from polyglot.builtins import string_or_bytes class ElementWriter: - def __init__(self, e, header=False, sourceEncoding="ascii", - spaceBeforeClose=True, outputEncodingName="UTF-16"): + def __init__(self, e, header=False, sourceEncoding='ascii', + spaceBeforeClose=True, outputEncodingName='UTF-16'): self.header = header self.e = e self.sourceEncoding=sourceEncoding @@ -17,9 +17,9 @@ class ElementWriter: if isinstance(rawText, bytes): rawText = rawText.decode(self.sourceEncoding) - text = rawText.replace("&", "&") - text = text.replace("<", "<") - text = text.replace(">", ">") + text = rawText.replace('&', '&') + text = text.replace('<', '<') + text = text.replace('>', '>') return text def _writeAttribute(self, f, name, value): diff --git a/src/calibre/ebooks/lrf/pylrs/pylrf.py b/src/calibre/ebooks/lrf/pylrs/pylrf.py index b27e3ca130..bce911d114 100644 --- a/src/calibre/ebooks/lrf/pylrs/pylrf.py +++ b/src/calibre/ebooks/lrf/pylrs/pylrf.py @@ -1,10 +1,10 @@ #!/usr/bin/env python -""" +''' pylrf.py -- very low level interface to create lrf files. See pylrs for higher level interface that can use this module to render books to lrf. -""" +''' import codecs import io import os @@ -15,7 +15,7 @@ from polyglot.builtins import iteritems, string_or_bytes from .pylrfopt import tagListOptimizer -PYLRF_VERSION = "1.0" +PYLRF_VERSION = '1.0' # # Acknowledgement: @@ -79,7 +79,7 @@ class LrfError(Exception): def writeByte(f, byte): - f.write(struct.pack("<B", byte)) + f.write(struct.pack('<B', byte)) def writeWord(f, word): @@ -87,31 +87,31 @@ def writeWord(f, word): raise LrfError('Cannot encode a number greater than 65535 in a word.') if int(word) < 0: raise LrfError('Cannot encode a number < 0 in a word: '+str(word)) - f.write(struct.pack("<H", int(word))) + f.write(struct.pack('<H', int(word))) def writeSignedWord(f, sword): - f.write(struct.pack("<h", int(float(sword)))) + f.write(struct.pack('<h', int(float(sword)))) def writeWords(f, *words): - f.write(struct.pack("<%dH" % len(words), *words)) + f.write(struct.pack('<%dH' % len(words), *words)) def writeDWord(f, dword): - f.write(struct.pack("<I", int(dword))) + f.write(struct.pack('<I', int(dword))) def writeDWords(f, *dwords): - f.write(struct.pack("<%dI" % len(dwords), *dwords)) + f.write(struct.pack('<%dI' % len(dwords), *dwords)) def writeQWord(f, qword): - f.write(struct.pack("<Q", qword)) + f.write(struct.pack('<Q', qword)) def writeZeros(f, nZeros): - f.write(b"\0" * nZeros) + f.write(b'\0' * nZeros) def writeString(f, s): @@ -125,7 +125,7 @@ def writeIdList(f, idList): def writeColor(f, color): # TODO: allow color names, web format - f.write(struct.pack(">I", int(color, 0))) + f.write(struct.pack('>I', int(color, 0))) def writeLineWidth(f, width): @@ -135,7 +135,7 @@ def writeLineWidth(f, width): def writeUnicode(f, string, encoding): if isinstance(string, bytes): string = string.decode(encoding) - string = string.encode("utf-16-le") + string = string.encode('utf-16-le') length = len(string) if length > 65535: raise LrfError('Cannot write strings longer than 65535 characters.') @@ -147,20 +147,20 @@ def writeRaw(f, string, encoding): if isinstance(string, bytes): string = string.decode(encoding) - string = string.encode("utf-16-le") + string = string.encode('utf-16-le') writeString(f, string) def writeRubyAA(f, rubyAA): ralign, radjust = rubyAA - radjust = {"line-edge":0x10, "none":0}[radjust] - ralign = {"start":1, "center":2}[ralign] + radjust = {'line-edge':0x10, 'none':0}[radjust] + ralign = {'start':1, 'center':2}[ralign] writeWord(f, ralign | radjust) def writeBgImage(f, bgInfo): imode, iid = bgInfo - imode = {"pfix": 0, "fix":1, "tile":2, "centering":3}[imode] + imode = {'pfix': 0, 'fix':1, 'tile':2, 'centering':3}[imode] writeWord(f, imode) writeDWord(f, iid) @@ -168,7 +168,7 @@ def writeBgImage(f, bgInfo): def writeEmpDots(f, dotsInfo, encoding): refDotsFont, dotsFontName, dotsCode = dotsInfo writeDWord(f, refDotsFont) - LrfTag("fontfacename", dotsFontName).write(f, encoding) + LrfTag('fontfacename', dotsFontName).write(f, encoding) writeWord(f, int(dotsCode, 0)) @@ -180,7 +180,7 @@ def writeRuledLine(f, lineInfo): writeColor(f, lineColor) -LRF_SIGNATURE = b"L\x00R\x00F\x00\x00\x00" +LRF_SIGNATURE = b'L\x00R\x00F\x00\x00\x00' # XOR_KEY = 48 XOR_KEY = 65024 # that's what lrf2lrs says -- not used, anyway... @@ -226,10 +226,10 @@ BINDING_DIRECTION_ENCODING = dict(Lr=1, Rl=16) TAG_INFO = dict( rawtext=(0, writeRaw), - ObjectStart=(0xF500, "<IH"), + ObjectStart=(0xF500, '<IH'), ObjectEnd=(0xF501,), # InfoLink (0xF502) - Link=(0xF503, "<I"), + Link=(0xF503, '<I'), StreamSize=(0xF504, writeDWord), StreamData=(0xF505, writeString), StreamEnd=(0xF506,), @@ -271,9 +271,9 @@ TAG_INFO = dict( {'None':0, 'curve':2, 'square':1}, writeWord), blockwidth=(0xF531, writeWord), blockheight=(0xF532, writeWord), - blockrule=(0xF533, {"horz-fixed":0x14, "horz-adjustable":0x12, - "vert-fixed":0x41, "vert-adjustable":0x21, - "block-fixed":0x44, "block-adjustable":0x22}, + blockrule=(0xF533, {'horz-fixed':0x14, 'horz-adjustable':0x12, + 'vert-fixed':0x41, 'vert-adjustable':0x21, + 'block-fixed':0x44, 'block-adjustable':0x22}, writeWord), bgcolor=(0xF534, writeColor), layout=(0xF535, {'TbRl':0x41, 'LrTb':0x34}, writeWord), @@ -289,11 +289,11 @@ TAG_INFO = dict( minipageheight=(0xF542, writeWord), yspace=(0xF546, writeWord), xspace=(0xF547, writeWord), - PutObj=(0xF549, "<HHI"), - ImageRect=(0xF54A, "<HHHH"), - ImageSize=(0xF54B, "<HH"), - RefObjId=(0xF54C, "<I"), - PageDiv=(0xF54E, "<HIHI"), + PutObj=(0xF549, '<HHI'), + ImageRect=(0xF54A, '<HHHH'), + ImageSize=(0xF54B, '<HH'), + RefObjId=(0xF54C, '<I'), + PageDiv=(0xF54E, '<HIHI'), StreamFlags=(0xF554, writeWord), Comment=(0xF555, writeUnicode), FontFilename=(0xF559, writeUnicode), @@ -304,7 +304,7 @@ TAG_INFO = dict( PushButtonEnd=(0xF567,), buttonactions=(0xF56A,), endbuttonactions=(0xF56B,), - jumpto=(0xF56C, "<II"), + jumpto=(0xF56C, '<II'), RuledLine=(0xF573, writeRuledLine), rubyaa=(0xF575, writeRubyAA), rubyoverhang=(0xF576, {'none':0, 'auto':1}, writeWord), @@ -312,8 +312,8 @@ TAG_INFO = dict( empdots=(0xF578, writeEmpDots), emplineposition=(0xF579, {'before':1, 'after':2}, writeWord), emplinetype=(0xF57A, LINE_TYPE_ENCODING, writeWord), - ChildPageTree=(0xF57B, "<I"), - ParentPageTree=(0xF57C, "<I"), + ChildPageTree=(0xF57B, '<I'), + ParentPageTree=(0xF57C, '<I'), Italic=(0xF581,), ItalicEnd=(0xF582,), pstart=(0xF5A1, writeDWord), # what goes in the dword? refesound @@ -348,7 +348,7 @@ TAG_INFO = dict( BoxEnd=(0xF5C7,), Space=(0xF5CA, writeSignedWord), textstring=(0xF5CC, writeUnicode), - Plot=(0xF5D1, "<HHII"), + Plot=(0xF5D1, '<HHII'), CR=(0xF5D2,), RegisterFont=(0xF5D8, writeDWord), setwaitprop=(0xF5DA, {'replay':1, 'noreplay':2}, writeWord), @@ -375,14 +375,14 @@ class LrfTag: try: tagInfo = TAG_INFO[name] except KeyError: - raise LrfError("tag name %s not recognized" % name) + raise LrfError('tag name %s not recognized' % name) self.name = name self.type = tagInfo[0] self.format = tagInfo[1:] if len(parameters) > 1: - raise LrfError("only one parameter allowed on tag %s" % name) + raise LrfError('only one parameter allowed on tag %s' % name) if len(parameters) == 0: self.parameter = None @@ -409,7 +409,7 @@ class LrfTag: else: if f in [writeUnicode, writeRaw, writeEmpDots]: if encoding is None: - raise LrfError("Tag requires encoding") + raise LrfError('Tag requires encoding') f(lrf, p, encoding) else: f(lrf, p) @@ -455,12 +455,12 @@ class LrfStreamBase: if optimize and uncompLen <= len(compStreamBuffer) + 4: flags &= ~STREAM_COMPRESSED else: - streamBuffer = struct.pack("<I", uncompLen) + compStreamBuffer + streamBuffer = struct.pack('<I', uncompLen) + compStreamBuffer - return [LrfTag("StreamFlags", flags & 0x01FF), - LrfTag("StreamSize", len(streamBuffer)), - LrfTag("StreamData", streamBuffer), - LrfTag("StreamEnd")] + return [LrfTag('StreamFlags', flags & 0x01FF), + LrfTag('StreamSize', len(streamBuffer)), + LrfTag('StreamData', streamBuffer), + LrfTag('StreamEnd')] class LrfTagStream(LrfStreamBase): @@ -493,7 +493,7 @@ class LrfFileStream(LrfStreamBase): def __init__(self, streamFlags, filename): LrfStreamBase.__init__(self, streamFlags) - with open(filename, "rb") as f: + with open(filename, 'rb') as f: self.streamData = f.read() @@ -501,7 +501,7 @@ class LrfObject: def __init__(self, name, objId): if objId <= 0: - raise LrfError("invalid objId for " + name) + raise LrfError('invalid objId for ' + name) self.name = name self.objId = objId @@ -509,10 +509,10 @@ class LrfObject: try: self.type = OBJECT_TYPE_ENCODING[name] except KeyError: - raise LrfError("object name %s not recognized" % name) + raise LrfError('object name %s not recognized' % name) def __str__(self): - return 'LRFObject: ' + self.name + ", " + str(self.objId) + return 'LRFObject: ' + self.name + ', ' + str(self.objId) def appendLrfTag(self, tag): self.tags.append(tag) @@ -533,55 +533,55 @@ class LrfObject: if name == 'rubyAlignAndAdjust': continue if name in { - "bgimagemode", "bgimageid", "rubyalign", "rubyadjust", - "empdotscode", "empdotsfontname", "refempdotsfont"}: + 'bgimagemode', 'bgimageid', 'rubyalign', 'rubyadjust', + 'empdotscode', 'empdotsfontname', 'refempdotsfont'}: composites[name] = value else: self.append(LrfTag(name, value)) - if "rubyalign" in composites or "rubyadjust" in composites: - ralign = composites.get("rubyalign", "none") - radjust = composites.get("rubyadjust", "start") - self.append(LrfTag("rubyaa", (ralign, radjust))) + if 'rubyalign' in composites or 'rubyadjust' in composites: + ralign = composites.get('rubyalign', 'none') + radjust = composites.get('rubyadjust', 'start') + self.append(LrfTag('rubyaa', (ralign, radjust))) - if "bgimagemode" in composites or "bgimageid" in composites: - imode = composites.get("bgimagemode", "fix") - iid = composites.get("bgimageid", 0) + if 'bgimagemode' in composites or 'bgimageid' in composites: + imode = composites.get('bgimagemode', 'fix') + iid = composites.get('bgimageid', 0) # for some reason, page style uses 0 for "fix" # we call this pfix to differentiate it - if genClass == "PageStyle" and imode == "fix": - imode = "pfix" + if genClass == 'PageStyle' and imode == 'fix': + imode = 'pfix' - self.append(LrfTag("bgimage", (imode, iid))) + self.append(LrfTag('bgimage', (imode, iid))) - if "empdotscode" in composites or "empdotsfontname" in composites or \ - "refempdotsfont" in composites: - dotscode = composites.get("empdotscode", "0x002E") - dotsfontname = composites.get("empdotsfontname", - "Dutch801 Rm BT Roman") - refdotsfont = composites.get("refempdotsfont", 0) - self.append(LrfTag("empdots", (refdotsfont, dotsfontname, + if 'empdotscode' in composites or 'empdotsfontname' in composites or \ + 'refempdotsfont' in composites: + dotscode = composites.get('empdotscode', '0x002E') + dotsfontname = composites.get('empdotsfontname', + 'Dutch801 Rm BT Roman') + refdotsfont = composites.get('refempdotsfont', 0) + self.append(LrfTag('empdots', (refdotsfont, dotsfontname, dotscode))) def write(self, lrf, encoding=None): # print "Writing object", self.name - LrfTag("ObjectStart", (self.objId, self.type)).write(lrf) + LrfTag('ObjectStart', (self.objId, self.type)).write(lrf) for tag in self.tags: tag.write(lrf, encoding) - LrfTag("ObjectEnd").write(lrf) + LrfTag('ObjectEnd').write(lrf) class LrfToc(LrfObject): - """ + ''' Table of contents. Format of toc is: [ (pageid, objid, string)...] - """ + ''' def __init__(self, objId, toc, se): - LrfObject.__init__(self, "TOC", objId) + LrfObject.__init__(self, 'TOC', objId) streamData = self._makeTocStream(toc, se) self._makeStreamTags(streamData) @@ -606,9 +606,9 @@ class LrfToc(LrfObject): for entry in toc: pageId, objId, label = entry if pageId <= 0: - raise LrfError("page id invalid in toc: " + label) + raise LrfError('page id invalid in toc: ' + label) if objId <= 0: - raise LrfError("textblock id invalid in toc: " + label) + raise LrfError('textblock id invalid in toc: ' + label) writeDWord(stream, pageId) writeDWord(stream, objId) @@ -644,9 +644,9 @@ class LrfWriter: self.height = 800 self.colorDepth = 24 self.tocObjId = 0 - self.docInfoXml = "" - self.thumbnailEncoding = "JPEG" - self.thumbnailData = b"" + self.docInfoXml = '' + self.thumbnailEncoding = 'JPEG' + self.thumbnailData = b'' self.objects = [] self.objectTable = [] @@ -670,7 +670,7 @@ class LrfWriter: def setRootObject(self, obj): if self.rootObjId != 0: - raise LrfError("root object already set") + raise LrfError('root object already set') self.rootObjId = obj.objId self.rootObj = obj @@ -679,16 +679,16 @@ class LrfWriter: if self.rootObj is None: raise LrfError("can't register font -- no root object") - self.rootObj.append(LrfTag("RegisterFont", id)) + self.rootObj.append(LrfTag('RegisterFont', id)) def setTocObject(self, obj): if self.tocObjId != 0: - raise LrfError("toc object already set") + raise LrfError('toc object already set') self.tocObjId = obj.objId def setThumbnailFile(self, filename, encoding=None): - with open(filename, "rb") as f: + with open(filename, 'rb') as f: self.thumbnailData = f.read() if encoding is None: @@ -696,7 +696,7 @@ class LrfWriter: encoding = encoding.upper() if encoding not in IMAGE_TYPE_ENCODING: - raise LrfError("unknown image type: " + encoding) + raise LrfError('unknown image type: ' + encoding) self.thumbnailEncoding = encoding @@ -708,7 +708,7 @@ class LrfWriter: def writeFile(self, lrf): if self.rootObjId == 0: - raise LrfError("no root object has been set") + raise LrfError('no root object has been set') self.writeHeader(lrf) self.writeObjects(lrf) @@ -730,7 +730,7 @@ class LrfWriter: writeZeros(lrf, 20) # 0x30 unknown writeDWord(lrf, self.tocObjId) writeDWord(lrf, 0) # 0x48 tocObjectOffset -- will be updated - docInfoXml = codecs.BOM_UTF8 + self.docInfoXml.encode("utf-8") + docInfoXml = codecs.BOM_UTF8 + self.docInfoXml.encode('utf-8') compDocInfo = zlib.compress(docInfoXml) writeWord(lrf, len(compDocInfo) + 4) writeWord(lrf, IMAGE_TYPE_ENCODING[self.thumbnailEncoding]) @@ -767,7 +767,7 @@ class LrfWriter: lrf.seek(0, 2) break else: - raise LrfError("toc object not in object table") + raise LrfError('toc object not in object table') def writeObjectTable(self, lrf): for tableEntry in self.objectTable: diff --git a/src/calibre/ebooks/lrf/pylrs/pylrfopt.py b/src/calibre/ebooks/lrf/pylrs/pylrfopt.py index 64648c565e..0461122e6b 100644 --- a/src/calibre/ebooks/lrf/pylrs/pylrfopt.py +++ b/src/calibre/ebooks/lrf/pylrs/pylrfopt.py @@ -2,7 +2,7 @@ def _optimize(tagList, tagName, conversion): # copy the tag of interest plus any text newTagList = [] for tag in tagList: - if tag.name == tagName or tag.name == "rawtext": + if tag.name == tagName or tag.name == 'rawtext': newTagList.append(tag) # now, eliminate any duplicates (leaving the last one) @@ -36,6 +36,6 @@ def tagListOptimizer(tagList): # should be: # fontsize=200 text oldSize = len(tagList) - _optimize(tagList, "fontsize", int) - _optimize(tagList, "fontweight", int) + _optimize(tagList, 'fontsize', int) + _optimize(tagList, 'fontweight', int) return oldSize - len(tagList) diff --git a/src/calibre/ebooks/lrf/pylrs/pylrs.py b/src/calibre/ebooks/lrf/pylrs/pylrs.py index e6956e14c7..f7d958f300 100644 --- a/src/calibre/ebooks/lrf/pylrs/pylrs.py +++ b/src/calibre/ebooks/lrf/pylrs/pylrs.py @@ -62,8 +62,8 @@ from .pylrf import ( LrfWriter, ) -DEFAULT_SOURCE_ENCODING = "cp1252" # default is us-windows character set -DEFAULT_GENREADING = "fs" # default is yes to both lrf and lrs +DEFAULT_SOURCE_ENCODING = 'cp1252' # default is us-windows character set +DEFAULT_GENREADING = 'fs' # default is yes to both lrf and lrs from calibre import __appname__, __version__, replace_entities from polyglot.builtins import iteritems, native_string_type, string_or_bytes @@ -83,28 +83,28 @@ def _checkExists(filename): def _formatXml(root): - """ A helper to make the LRS output look nicer. """ + ''' A helper to make the LRS output look nicer. ''' for elem in root.iter(): if len(elem) > 0 and (not elem.text or not elem.text.strip()): - elem.text = "\n" + elem.text = '\n' if not elem.tail or not elem.tail.strip(): - elem.tail = "\n" + elem.tail = '\n' def ElementWithText(tag, text, **extra): - """ A shorthand function to create Elements with text. """ + ''' A shorthand function to create Elements with text. ''' e = Element(tag, **extra) e.text = text return e def ElementWithReading(tag, text, reading=False): - """ A helper function that creates reading attributes. """ + ''' A helper function that creates reading attributes. ''' # note: old lrs2lrf parser only allows reading = "" if text is None: - readingText = "" + readingText = '' elif isinstance(text, string_or_bytes): readingText = text else: @@ -113,12 +113,12 @@ def ElementWithReading(tag, text, reading=False): text = text[0] if not reading: - readingText = "" + readingText = '' return ElementWithText(tag, text, reading=readingText) def appendTextElements(e, contentsList, se): - """ A helper function to convert text streams into the proper elements. """ + ''' A helper function to convert text streams into the proper elements. ''' def uconcat(text, newText, se): if isinstance(text, bytes): @@ -128,7 +128,7 @@ def appendTextElements(e, contentsList, se): return text + newText - e.text = "" + e.text = '' lastElement = None for content in contentsList: @@ -137,7 +137,7 @@ def appendTextElements(e, contentsList, se): if newElement is None: continue lastElement = newElement - lastElement.tail = "" + lastElement.tail = '' e.append(lastElement) else: if lastElement is None: @@ -147,7 +147,7 @@ def appendTextElements(e, contentsList, se): class Delegator: - """ A mixin class to create delegated methods that create elements. """ + ''' A mixin class to create delegated methods that create elements. ''' def __init__(self, delegates): self.delegates = delegates @@ -161,7 +161,7 @@ class Delegator: for m in methods: setattr(self, m, getattr(d, m)) - """ + ''' for setting in d.getSettings(): if isinstance(setting, string_or_bytes): setting = (d, setting) @@ -169,7 +169,7 @@ class Delegator: self.delegatedSettingsDict.setdefault(setting[1], []) delegates.append(setting[0]) self.delegatedSettings.append(setting) - """ + ''' def applySetting(self, name, value, testValid=False): applied = False @@ -178,7 +178,7 @@ class Delegator: applied = True for d in self.delegates: - if hasattr(d, "applySetting"): + if hasattr(d, 'applySetting'): applied = applied or d.applySetting(name, value) else: if name in d.getSettings(): @@ -186,20 +186,20 @@ class Delegator: applied = True if testValid and not applied: - raise LrsError("setting %s not valid" % name) + raise LrsError('setting %s not valid' % name) return applied def applySettings(self, settings, testValid=False): for (setting, value) in settings.items(): self.applySetting(setting, value, testValid) - """ + ''' if setting not in self.delegatedSettingsDict: raise LrsError, "setting %s not valid" % setting delegates = self.delegatedSettingsDict[setting] for d in delegates: setattr(d, setting, value) - """ + ''' def appendDelegates(self, element, sourceEncoding): for d in self.delegates: @@ -230,7 +230,7 @@ class Delegator: class LrsAttributes: - """ A mixin class to handle default and user supplied attributes. """ + ''' A mixin class to handle default and user supplied attributes. ''' def __init__(self, defaults, alsoAllow=None, **settings): if alsoAllow is None: @@ -238,7 +238,7 @@ class LrsAttributes: self.attrs = defaults.copy() for (name, value) in settings.items(): if name not in self.attrs and name not in alsoAllow: - raise LrsError("%s does not support setting %s" % + raise LrsError('%s does not support setting %s' % (self.__class__.__name__, name)) if isinstance(value, int): value = str(value) @@ -246,9 +246,9 @@ class LrsAttributes: class LrsContainer: - """ This class is a mixin class for elements that are contained in or + ''' This class is a mixin class for elements that are contained in or contain an unknown number of other elements. - """ + ''' def __init__(self, validChildren): self.parent = None @@ -284,15 +284,15 @@ class LrsContainer: def setParent(self, parent): if self.parent is not None: - raise LrsError("object already has parent") + raise LrsError('object already has parent') self.parent = parent def append(self, content, convertText=True): - """ + ''' Appends valid objects to container. Can auto-covert text strings to Text objects. - """ + ''' for validChild in self.validChildren: if isinstance(content, validChild): break @@ -321,7 +321,7 @@ class LrsContainer: class LrsObject: - """ A mixin class for elements that need an object id. """ + ''' A mixin class for elements that need an object id. ''' nextObjId = 0 @classmethod @@ -337,18 +337,18 @@ class LrsObject: def assignId(self): if self.objId != 0: - raise LrsError("id already assigned to " + self.__class__.__name__) + raise LrsError('id already assigned to ' + self.__class__.__name__) self.objId = LrsObject.getNextObjId() - def lrsObjectElement(self, name, objlabel="objlabel", labelName=None, + def lrsObjectElement(self, name, objlabel='objlabel', labelName=None, labelDecorate=True, **settings): element = Element(name) - element.attrib["objid"] = str(self.objId) + element.attrib['objid'] = str(self.objId) if labelName is None: labelName = name if labelDecorate: - label = "%s.%d" % (labelName, self.objId) + label = '%s.%d' % (labelName, self.objId) else: label = str(self.objId) element.attrib[objlabel] = label @@ -406,8 +406,8 @@ class Book(Delegator): self.parent = None # we are the top of the parent chain - if "thumbnail" in settings: - _checkExists(settings["thumbnail"]) + if 'thumbnail' in settings: + _checkExists(settings['thumbnail']) # highly experimental -- use with caution self.optimizeTags = optimizeTags @@ -440,8 +440,8 @@ class Book(Delegator): self.sourceencoding = None # apply default settings - self.applySetting("genreading", DEFAULT_GENREADING) - self.applySetting("sourceencoding", DEFAULT_SOURCE_ENCODING) + self.applySetting('genreading', DEFAULT_GENREADING) + self.applySetting('sourceencoding', DEFAULT_SOURCE_ENCODING) self.applySettings(settings, testValid=True) @@ -524,14 +524,14 @@ class Book(Delegator): self.append(f) def getSettings(self): - return ["sourceencoding"] + return ['sourceencoding'] def append(self, content): - """ Find and invoke the correct appender for this content. """ + ''' Find and invoke the correct appender for this content. ''' className = content.__class__.__name__ try: - method = getattr(self, "append" + className) + method = getattr(self, 'append' + className) except AttributeError: raise LrsError("can't append %s to Book" % className) @@ -593,16 +593,16 @@ class Book(Delegator): ts.attrs['fontsize'] = rescale(ts.attrs['fontsize']) ts.attrs['baselineskip'] = rescale(ts.attrs['baselineskip']) - def renderLrs(self, lrsFile, encoding="UTF-8"): + def renderLrs(self, lrsFile, encoding='UTF-8'): if isinstance(lrsFile, string_or_bytes): - lrsFile = codecs.open(lrsFile, "wb", encoding=encoding) + lrsFile = codecs.open(lrsFile, 'wb', encoding=encoding) self.render(lrsFile, outputEncodingName=encoding) lrsFile.close() def renderLrf(self, lrfFile): self.appendReferencedObjects(self) if isinstance(lrfFile, string_or_bytes): - lrfFile = open(lrfFile, "wb") + lrfFile = open(lrfFile, 'wb') lrfWriter = LrfWriter(self.sourceencoding) lrfWriter.optimizeTags = self.optimizeTags @@ -613,13 +613,13 @@ class Book(Delegator): lrfFile.close() def toElement(self, se): - root = Element("BBeBXylog", version="1.0") - root.append(Element("Property")) + root = Element('BBeBXylog', version='1.0') + root.append(Element('Property')) self.appendDelegates(root, self.sourceencoding) return root def render(self, f, outputEncodingName='UTF-8'): - """ Write the book as an LRS to file f. """ + ''' Write the book as an LRS to file f. ''' self.appendReferencedObjects(self) @@ -635,46 +635,46 @@ class Book(Delegator): class BookInformation(Delegator): - """ Just a container for the Info and TableOfContents elements. """ + ''' Just a container for the Info and TableOfContents elements. ''' def __init__(self): Delegator.__init__(self, [Info(), TableOfContents()]) def toElement(self, se): - bi = Element("BookInformation") + bi = Element('BookInformation') self.appendDelegates(bi, se) return bi class Info(Delegator): - """ Just a container for the BookInfo and DocInfo elements. """ + ''' Just a container for the BookInfo and DocInfo elements. ''' def __init__(self): self.genreading = DEFAULT_GENREADING Delegator.__init__(self, [BookInfo(), DocInfo()]) def getSettings(self): - return ["genreading"] # + self.delegatedSettings + return ['genreading'] # + self.delegatedSettings def toElement(self, se): - info = Element("Info", version="1.1") + info = Element('Info', version='1.1') info.append( - self.delegates[0].toElement(se, reading="s" in self.genreading)) + self.delegates[0].toElement(se, reading='s' in self.genreading)) info.append(self.delegates[1].toElement(se)) return info def toLrf(self, lrfWriter): # this info is set in XML form in the LRF - info = Element("Info", version="1.1") + info = Element('Info', version='1.1') # self.appendDelegates(info) info.append( - self.delegates[0].toElement(lrfWriter.getSourceEncoding(), reading="f" in self.genreading)) + self.delegates[0].toElement(lrfWriter.getSourceEncoding(), reading='f' in self.genreading)) info.append(self.delegates[1].toElement(lrfWriter.getSourceEncoding())) # look for the thumbnail file and get the filename - tnail = info.find("DocInfo/CThumbnail") + tnail = info.find('DocInfo/CThumbnail') if tnail is not None: - lrfWriter.setThumbnailFile(tnail.get("file")) + lrfWriter.setThumbnailFile(tnail.get('file')) # does not work: info.remove(tnail) _formatXml(info) @@ -685,8 +685,8 @@ class Info(Delegator): f = io.BytesIO() tree.write(f, encoding=native_string_type('utf-8'), xml_declaration=True) xmlInfo = f.getvalue().decode('utf-8') - xmlInfo = re.sub(r"<CThumbnail.*?>\n", "", xmlInfo) - xmlInfo = xmlInfo.replace("SumPage>", "Page>") + xmlInfo = re.sub(r'<CThumbnail.*?>\n', '', xmlInfo) + xmlInfo = xmlInfo.replace('SumPage>', 'Page>') lrfWriter.docInfoXml = xmlInfo @@ -699,24 +699,24 @@ class TableOfContents: pass def getMethods(self): - return ["addTocEntry"] + return ['addTocEntry'] def getSettings(self): return [] def addTocEntry(self, tocLabel, textBlock): if not isinstance(textBlock, (Canvas, TextBlock, ImageBlock, RuledLine)): - raise LrsError("TOC destination must be a Canvas, TextBlock, ImageBlock or RuledLine"+ - " not a " + str(type(textBlock))) + raise LrsError('TOC destination must be a Canvas, TextBlock, ImageBlock or RuledLine'+ + ' not a ' + str(type(textBlock))) if textBlock.parent is None: - raise LrsError("TOC text block must be already appended to a page") + raise LrsError('TOC text block must be already appended to a page') if False and textBlock.parent.parent is None: - raise LrsError("TOC destination page must be already appended to a book") + raise LrsError('TOC destination page must be already appended to a book') if not hasattr(textBlock.parent, 'objId'): - raise LrsError("TOC destination must be appended to a container with an objID") + raise LrsError('TOC destination must be appended to a container with an objID') for tl in self.tocEntries: if tl.label == tocLabel and tl.textBlock == textBlock: @@ -729,7 +729,7 @@ class TableOfContents: if len(self.tocEntries) == 0: return None - toc = Element("TOC") + toc = Element('TOC') for t in self.tocEntries: toc.append(t.toElement(se)) @@ -756,7 +756,7 @@ class TocLabel: self.textBlock = textBlock def toElement(self, se): - return ElementWithText("TocLabel", self.label, + return ElementWithText('TocLabel', self.label, refobj=str(self.textBlock.objId), refpage=str(self.textBlock.parent.objId)) @@ -764,13 +764,13 @@ class TocLabel: class BookInfo: def __init__(self): - self.title = "Untitled" - self.author = "Anonymous" + self.title = 'Untitled' + self.author = 'Anonymous' self.bookid = None self.pi = None self.isbn = None self.publisher = None - self.freetext = "\n\n" + self.freetext = '\n\n' self.label = None self.category = None self.classification = None @@ -782,34 +782,34 @@ class BookInfo: return [] def getSettings(self): - return ["author", "title", "bookid", "isbn", "publisher", - "freetext", "label", "category", "classification"] + return ['author', 'title', 'bookid', 'isbn', 'publisher', + 'freetext', 'label', 'category', 'classification'] def _appendISBN(self, bi): - pi = Element("ProductIdentifier") - isbnElement = ElementWithText("ISBNPrintable", self.isbn) - isbnValueElement = ElementWithText("ISBNValue", - self.isbn.replace("-", "")) + pi = Element('ProductIdentifier') + isbnElement = ElementWithText('ISBNPrintable', self.isbn) + isbnValueElement = ElementWithText('ISBNValue', + self.isbn.replace('-', '')) pi.append(isbnElement) pi.append(isbnValueElement) bi.append(pi) def toElement(self, se, reading=True): - bi = Element("BookInfo") - bi.append(ElementWithReading("Title", self.title, reading=reading)) - bi.append(ElementWithReading("Author", self.author, reading=reading)) - bi.append(ElementWithText("BookID", self.bookid)) + bi = Element('BookInfo') + bi.append(ElementWithReading('Title', self.title, reading=reading)) + bi.append(ElementWithReading('Author', self.author, reading=reading)) + bi.append(ElementWithText('BookID', self.bookid)) if self.isbn is not None: self._appendISBN(bi) if self.publisher is not None: - bi.append(ElementWithReading("Publisher", self.publisher)) + bi.append(ElementWithReading('Publisher', self.publisher)) - bi.append(ElementWithReading("Label", self.label, reading=reading)) - bi.append(ElementWithText("Category", self.category)) - bi.append(ElementWithText("Classification", self.classification)) - bi.append(ElementWithText("FreeText", self.freetext)) + bi.append(ElementWithReading('Label', self.label, reading=reading)) + bi.append(ElementWithText('Category', self.category)) + bi.append(ElementWithText('Classification', self.classification)) + bi.append(ElementWithText('FreeText', self.freetext)) return bi @@ -817,11 +817,11 @@ class DocInfo: def __init__(self): self.thumbnail = None - self.language = "en" + self.language = 'en' self.creator = None self.creationdate = str(isoformat(date.today())) - self.producer = "%s v%s"%(__appname__, __version__) - self.numberofpages = "0" + self.producer = '%s v%s'%(__appname__, __version__) + self.numberofpages = '0' def appendReferencedObjects(self, parent): pass @@ -830,20 +830,20 @@ class DocInfo: return [] def getSettings(self): - return ["thumbnail", "language", "creator", "creationdate", - "producer", "numberofpages"] + return ['thumbnail', 'language', 'creator', 'creationdate', + 'producer', 'numberofpages'] def toElement(self, se): - docInfo = Element("DocInfo") + docInfo = Element('DocInfo') if self.thumbnail is not None: - docInfo.append(Element("CThumbnail", file=self.thumbnail)) + docInfo.append(Element('CThumbnail', file=self.thumbnail)) - docInfo.append(ElementWithText("Language", self.language)) - docInfo.append(ElementWithText("Creator", self.creator)) - docInfo.append(ElementWithText("CreationDate", self.creationdate)) - docInfo.append(ElementWithText("Producer", self.producer)) - docInfo.append(ElementWithText("SumPage", str(self.numberofpages))) + docInfo.append(ElementWithText('Language', self.language)) + docInfo.append(ElementWithText('Creator', self.creator)) + docInfo.append(ElementWithText('CreationDate', self.creationdate)) + docInfo.append(ElementWithText('Producer', self.producer)) + docInfo.append(ElementWithText('SumPage', str(self.numberofpages))) return docInfo @@ -853,7 +853,7 @@ class Main(LrsContainer): LrsContainer.__init__(self, [Page]) def getMethods(self): - return ["appendPage", "Page"] + return ['appendPage', 'Page'] def getSettings(self): return [] @@ -889,8 +889,8 @@ class Main(LrsContainer): # create a page tree object - pageTree = LrfObject("PageTree", pageTreeId) - pageTree.appendLrfTag(LrfTag("PageList", pageIds)) + pageTree = LrfObject('PageTree', pageTreeId) + pageTree.appendLrfTag(LrfTag('PageList', pageIds)) lrfWriter.append(pageTree) @@ -901,7 +901,7 @@ class Solos(LrsContainer): LrsContainer.__init__(self, [Solo]) def getMethods(self): - return ["appendSolo", "Solo"] + return ['appendSolo', 'Solo'] def getSettings(self): return [] @@ -934,7 +934,7 @@ class Solo(Main): class Template: - """ Does nothing that I know of. """ + ''' Does nothing that I know of. ''' def appendReferencedObjects(self, parent): pass @@ -946,8 +946,8 @@ class Template: return [] def toElement(self, se): - t = Element("Template") - t.attrib["version"] = "1.0" + t = Element('Template') + t.attrib['version'] = '1.0' return t def toLrf(self, lrfWriter): @@ -956,25 +956,25 @@ class Template: class StyleDefault(LrsAttributes): - """ + ''' Supply some defaults for all TextBlocks. The legal values are a subset of what is allowed on a TextBlock -- ruby, emphasis, and waitprop settings. - """ - defaults = dict(rubyalign="start", rubyadjust="none", - rubyoverhang="none", empdotsposition="before", - empdotsfontname="Dutch801 Rm BT Roman", - empdotscode="0x002e", emplineposition="after", - emplinetype="solid", setwaitprop="noreplay") + ''' + defaults = dict(rubyalign='start', rubyadjust='none', + rubyoverhang='none', empdotsposition='before', + empdotsfontname='Dutch801 Rm BT Roman', + empdotscode='0x002e', emplineposition='after', + emplinetype='solid', setwaitprop='noreplay') - alsoAllow = ["refempdotsfont", "rubyAlignAndAdjust"] + alsoAllow = ['refempdotsfont', 'rubyAlignAndAdjust'] def __init__(self, **settings): LrsAttributes.__init__(self, self.defaults, alsoAllow=self.alsoAllow, **settings) def toElement(self, se): - return Element("SetDefault", self.attrs) + return Element('SetDefault', self.attrs) class Style(LrsContainer, Delegator): @@ -990,8 +990,8 @@ class Style(LrsContainer, Delegator): LrsContainer.appendReferencedObjects(self, parent) def getMethods(self): - return ["PageStyle", "TextStyle", "BlockStyle", - "appendPageStyle", "appendTextStyle", "appendBlockStyle"] + \ + return ['PageStyle', 'TextStyle', 'BlockStyle', + 'appendPageStyle', 'appendTextStyle', 'appendBlockStyle'] + \ self.delegatedMethods def getSettings(self): @@ -1013,7 +1013,7 @@ class Style(LrsContainer, Delegator): return bs def toElement(self, se): - style = Element("Style") + style = Element('Style') style.append(self.bookStyle.toElement(se)) for content in self.contents: @@ -1038,10 +1038,10 @@ class BookStyle(LrsObject, LrsContainer): self.appendFont = self.append def getSettings(self): - return ["styledefault", "booksetting"] + return ['styledefault', 'booksetting'] def getMethods(self): - return ["Font", "appendFont"] + return ['Font', 'appendFont'] def Font(self, *args, **kwargs): f = Font(*args, **kwargs) @@ -1049,7 +1049,7 @@ class BookStyle(LrsObject, LrsContainer): return def toElement(self, se): - bookStyle = self.lrsObjectElement("BookStyle", objlabel="stylelabel", + bookStyle = self.lrsObjectElement('BookStyle', objlabel='stylelabel', labelDecorate=False) bookStyle.append(self.styledefault.toElement(se)) bookStyle.append(self.booksetting.toElement(se)) @@ -1059,8 +1059,8 @@ class BookStyle(LrsObject, LrsContainer): return bookStyle def toLrf(self, lrfWriter): - bookAtr = LrfObject("BookAtr", self.objId) - bookAtr.appendLrfTag(LrfTag("ChildPageTree", lrfWriter.getPageTreeId())) + bookAtr = LrfObject('BookAtr', self.objId) + bookAtr.appendLrfTag(LrfTag('ChildPageTree', lrfWriter.getPageTreeId())) bookAtr.appendTagDict(self.styledefault.attrs) self.booksetting.toLrf(lrfWriter) @@ -1075,25 +1075,25 @@ class BookStyle(LrsObject, LrsContainer): class BookSetting(LrsAttributes): def __init__(self, **settings): - defaults = dict(bindingdirection="Lr", dpi="1660", - screenheight="800", screenwidth="600", colordepth="24") + defaults = dict(bindingdirection='Lr', dpi='1660', + screenheight='800', screenwidth='600', colordepth='24') LrsAttributes.__init__(self, defaults, **settings) def toLrf(self, lrfWriter): a = self.attrs - lrfWriter.dpi = int(a["dpi"]) + lrfWriter.dpi = int(a['dpi']) lrfWriter.bindingdirection = \ - BINDING_DIRECTION_ENCODING[a["bindingdirection"]] - lrfWriter.height = int(a["screenheight"]) - lrfWriter.width = int(a["screenwidth"]) - lrfWriter.colorDepth = int(a["colordepth"]) + BINDING_DIRECTION_ENCODING[a['bindingdirection']] + lrfWriter.height = int(a['screenheight']) + lrfWriter.width = int(a['screenwidth']) + lrfWriter.colorDepth = int(a['colordepth']) def toElement(self, se): - return Element("BookSetting", self.attrs) + return Element('BookSetting', self.attrs) class LrsStyle(LrsObject, LrsAttributes, LrsContainer): - """ A mixin class for styles. """ + ''' A mixin class for styles. ''' def __init__(self, elementName, defaults=None, alsoAllow=None, **overrides): if defaults is None: @@ -1111,7 +1111,7 @@ class LrsStyle(LrsObject, LrsAttributes, LrsContainer): def update(self, settings): for name, value in settings.items(): if name not in self.__class__.validSettings: - raise LrsError(f"{name} not a valid setting for {self.__class__.__name__}") + raise LrsError(f'{name} not a valid setting for {self.__class__.__name__}') self.attrs[name] = value def getLabel(self): @@ -1135,7 +1135,7 @@ class LrsStyle(LrsObject, LrsAttributes, LrsContainer): class TextStyle(LrsStyle): - """ + ''' The text style of a TextBlock. Default is 10 pt. Times Roman. Setting Value Default @@ -1149,27 +1149,27 @@ class TextStyle(LrsStyle): linespace points * 10 10 (min space btw. lines?) wordspace points * 10 25 (min space btw. each word) - """ + ''' baseDefaults = dict( - columnsep="0", charspace="0", - textlinewidth="2", align="head", linecolor="0x00000000", - column="1", fontsize="100", fontwidth="-10", fontescapement="0", - fontorientation="0", fontweight="400", - fontfacename="Dutch801 Rm BT Roman", - textcolor="0x00000000", wordspace="25", letterspace="0", - baselineskip="120", linespace="10", parindent="0", parskip="0", - textbgcolor="0xFF000000") + columnsep='0', charspace='0', + textlinewidth='2', align='head', linecolor='0x00000000', + column='1', fontsize='100', fontwidth='-10', fontescapement='0', + fontorientation='0', fontweight='400', + fontfacename='Dutch801 Rm BT Roman', + textcolor='0x00000000', wordspace='25', letterspace='0', + baselineskip='120', linespace='10', parindent='0', parskip='0', + textbgcolor='0xFF000000') - alsoAllow = ["empdotscode", "empdotsfontname", "refempdotsfont", - "rubyadjust", "rubyalign", "rubyoverhang", - "empdotsposition", 'emplinetype', 'emplineposition'] + alsoAllow = ['empdotscode', 'empdotsfontname', 'refempdotsfont', + 'rubyadjust', 'rubyalign', 'rubyoverhang', + 'empdotsposition', 'emplinetype', 'emplineposition'] validSettings = list(baseDefaults) + alsoAllow defaults = baseDefaults.copy() def __init__(self, **overrides): - LrsStyle.__init__(self, "TextStyle", self.defaults, + LrsStyle.__init__(self, 'TextStyle', self.defaults, alsoAllow=self.alsoAllow, **overrides) def copy(self): @@ -1179,7 +1179,7 @@ class TextStyle(LrsStyle): class BlockStyle(LrsStyle): - """ + ''' The block style of a TextBlock. Default is an expandable 560 pixel wide area with no space for headers or footers. @@ -1187,19 +1187,19 @@ class BlockStyle(LrsStyle): -------- ----- ------- blockwidth pixels 560 sidemargin pixels 0 - """ + ''' baseDefaults = dict( - bgimagemode="fix", framemode="square", blockwidth="560", - blockheight="100", blockrule="horz-adjustable", layout="LrTb", - framewidth="0", framecolor="0x00000000", topskip="0", - sidemargin="0", footskip="0", bgcolor="0xFF000000") + bgimagemode='fix', framemode='square', blockwidth='560', + blockheight='100', blockrule='horz-adjustable', layout='LrTb', + framewidth='0', framecolor='0x00000000', topskip='0', + sidemargin='0', footskip='0', bgcolor='0xFF000000') validSettings = baseDefaults.keys() defaults = baseDefaults.copy() def __init__(self, **overrides): - LrsStyle.__init__(self, "BlockStyle", self.defaults, **overrides) + LrsStyle.__init__(self, 'BlockStyle', self.defaults, **overrides) def copy(self): tb = BlockStyle() @@ -1208,35 +1208,35 @@ class BlockStyle(LrsStyle): class PageStyle(LrsStyle): - """ + ''' Setting Value Default -------- ----- ------- evensidemargin pixels 20 oddsidemargin pixels 20 topmargin pixels 20 - """ + ''' baseDefaults = dict( - topmargin="20", headheight="0", headsep="0", - oddsidemargin="20", textheight="747", textwidth="575", - footspace="0", evensidemargin="20", footheight="0", - layout="LrTb", bgimagemode="fix", pageposition="any", - setwaitprop="noreplay", setemptyview="show") + topmargin='20', headheight='0', headsep='0', + oddsidemargin='20', textheight='747', textwidth='575', + footspace='0', evensidemargin='20', footheight='0', + layout='LrTb', bgimagemode='fix', pageposition='any', + setwaitprop='noreplay', setemptyview='show') - alsoAllow = ["header", "evenheader", "oddheader", - "footer", "evenfooter", "oddfooter"] + alsoAllow = ['header', 'evenheader', 'oddheader', + 'footer', 'evenfooter', 'oddfooter'] validSettings = list(baseDefaults) + alsoAllow defaults = baseDefaults.copy() @classmethod def translateHeaderAndFooter(selfClass, parent, settings): - selfClass._fixup(parent, "header", settings) - selfClass._fixup(parent, "footer", settings) + selfClass._fixup(parent, 'header', settings) + selfClass._fixup(parent, 'footer', settings) @classmethod def _fixup(selfClass, parent, basename, settings): - evenbase = "even" + basename - oddbase = "odd" + basename + evenbase = 'even' + basename + oddbase = 'odd' + basename if basename in settings: baseObj = settings[basename] del settings[basename] @@ -1247,14 +1247,14 @@ class PageStyle(LrsStyle): del settings[evenbase] if evenObj.parent is None: parent.append(evenObj) - settings[evenbase + "id"] = str(evenObj.objId) + settings[evenbase + 'id'] = str(evenObj.objId) if oddbase in settings: oddObj = settings[oddbase] del settings[oddbase] if oddObj.parent is None: parent.append(oddObj) - settings[oddbase + "id"] = str(oddObj.objId) + settings[oddbase + 'id'] = str(oddObj.objId) def appendReferencedObjects(self, parent): if self.objectsAppended: @@ -1264,15 +1264,15 @@ class PageStyle(LrsStyle): def __init__(self, **settings): # self.fixHeaderSettings(settings) - LrsStyle.__init__(self, "PageStyle", self.defaults, + LrsStyle.__init__(self, 'PageStyle', self.defaults, alsoAllow=self.alsoAllow, **settings) class Page(LrsObject, LrsContainer): - """ + ''' Pages are added to Books. Pages can be supplied a PageStyle. If they are not, Page.defaultPageStyle will be used. - """ + ''' defaultPageStyle = PageStyle() def __init__(self, pageStyle=defaultPageStyle, **settings): @@ -1285,7 +1285,7 @@ class Page(LrsObject, LrsContainer): for settingName in settings.keys(): if settingName not in PageStyle.defaults and \ settingName not in PageStyle.alsoAllow: - raise LrsError("setting %s not allowed on Page" % settingName) + raise LrsError('setting %s not allowed on Page' % settingName) self.settings = settings.copy() @@ -1310,19 +1310,19 @@ class Page(LrsObject, LrsContainer): return bs def TextBlock(self, *args, **kwargs): - """ Create and append a new text block (shortcut). """ + ''' Create and append a new text block (shortcut). ''' tb = TextBlock(*args, **kwargs) self.append(tb) return tb def ImageBlock(self, *args, **kwargs): - """ Create and append and new Image block (shorthand). """ + ''' Create and append and new Image block (shorthand). ''' ib = ImageBlock(*args, **kwargs) self.append(ib) return ib def addLrfObject(self, objId): - self.stream.appendLrfTag(LrfTag("Link", objId)) + self.stream.appendLrfTag(LrfTag('Link', objId)) def appendLrfTag(self, lrfTag): self.stream.appendLrfTag(lrfTag) @@ -1334,27 +1334,27 @@ class Page(LrsObject, LrsContainer): # Parent page tree id # stream of tags - p = LrfObject("Page", self.objId) + p = LrfObject('Page', self.objId) lrfWriter.append(p) pageContent = set() self.stream = LrfTagStream(0) for content in self.contents: content.toLrfContainer(lrfWriter, self) - if hasattr(content, "getReferencedObjIds"): + if hasattr(content, 'getReferencedObjIds'): pageContent.update(content.getReferencedObjIds()) # print "page contents:", pageContent # ObjectList not needed and causes slowdown in SONY LRF renderer # p.appendLrfTag(LrfTag("ObjectList", pageContent)) - p.appendLrfTag(LrfTag("Link", self.pageStyle.objId)) - p.appendLrfTag(LrfTag("ParentPageTree", lrfWriter.getPageTreeId())) + p.appendLrfTag(LrfTag('Link', self.pageStyle.objId)) + p.appendLrfTag(LrfTag('ParentPageTree', lrfWriter.getPageTreeId())) p.appendTagDict(self.settings) p.appendLrfTags(self.stream.getStreamTags(lrfWriter.getSourceEncoding())) def toElement(self, sourceEncoding): - page = self.lrsObjectElement("Page") - page.set("pagestyle", self.pageStyle.getLabel()) + page = self.lrsObjectElement('Page') + page.set('pagestyle', self.pageStyle.getLabel()) page.attrib.update(self.settings) for content in self.contents: @@ -1364,12 +1364,12 @@ class Page(LrsObject, LrsContainer): class TextBlock(LrsObject, LrsContainer): - """ + ''' TextBlocks are added to Pages. They hold Paragraphs or CRs. If a TextBlock is used in a header, it should be appended to the Book, not to a specific Page. - """ + ''' defaultTextStyle = TextStyle() defaultBlockStyle = BlockStyle() @@ -1396,7 +1396,7 @@ class TextBlock(LrsObject, LrsContainer): elif name == 'toclabel': self.tocLabel = value else: - raise LrsError("%s not a valid setting for TextBlock" % name) + raise LrsError('%s not a valid setting for TextBlock' % name) self.textStyle = textStyle self.blockStyle = blockStyle @@ -1415,25 +1415,25 @@ class TextBlock(LrsObject, LrsContainer): LrsContainer.appendReferencedObjects(self, parent) def Paragraph(self, *args, **kwargs): - """ + ''' Create and append a Paragraph to this TextBlock. A CR is automatically inserted after the Paragraph. To avoid this behavior, create the Paragraph and append it to the TextBlock in a separate call. - """ + ''' p = Paragraph(*args, **kwargs) self.append(p) self.append(CR()) return p def toElement(self, sourceEncoding): - tb = self.lrsObjectElement("TextBlock", labelName="Block") + tb = self.lrsObjectElement('TextBlock', labelName='Block') tb.attrib.update(self.textSettings) tb.attrib.update(self.blockSettings) - tb.set("textstyle", self.textStyle.getLabel()) - tb.set("blockstyle", self.blockStyle.getLabel()) - if hasattr(self, "tocLabel"): - tb.set("toclabel", self.tocLabel) + tb.set('textstyle', self.textStyle.getLabel()) + tb.set('blockstyle', self.blockStyle.getLabel()) + if hasattr(self, 'tocLabel'): + tb.set('toclabel', self.tocLabel) for content in self.contents: tb.append(content.toElement(sourceEncoding)) @@ -1444,7 +1444,7 @@ class TextBlock(LrsObject, LrsContainer): ids = [self.objId, self.extraId, self.blockStyle.objId, self.textStyle.objId] for content in self.contents: - if hasattr(content, "getReferencedObjIds"): + if hasattr(content, 'getReferencedObjIds'): ids.extend(content.getReferencedObjIds()) return ids @@ -1456,16 +1456,16 @@ class TextBlock(LrsObject, LrsContainer): # id really belongs to the outer block extraId = LrsObject.getNextObjId() - b = LrfObject("Block", self.objId) - b.appendLrfTag(LrfTag("Link", self.blockStyle.objId)) + b = LrfObject('Block', self.objId) + b.appendLrfTag(LrfTag('Link', self.blockStyle.objId)) b.appendLrfTags( - LrfTagStream(0, [LrfTag("Link", extraId)]).getStreamTags(lrfWriter.getSourceEncoding())) + LrfTagStream(0, [LrfTag('Link', extraId)]).getStreamTags(lrfWriter.getSourceEncoding())) b.appendTagDict(self.blockSettings) container.addLrfObject(b.objId) lrfWriter.append(b) - tb = LrfObject("TextBlock", extraId) - tb.appendLrfTag(LrfTag("Link", self.textStyle.objId)) + tb = LrfObject('TextBlock', extraId) + tb.appendLrfTag(LrfTag('Link', self.textStyle.objId)) tb.appendTagDict(self.textSettings) stream = LrfTagStream(STREAM_COMPRESSED) @@ -1512,19 +1512,19 @@ class Paragraph(LrsContainer): def getReferencedObjIds(self): ids = [] for content in self.contents: - if hasattr(content, "getReferencedObjIds"): + if hasattr(content, 'getReferencedObjIds'): ids.extend(content.getReferencedObjIds()) return ids def toLrfContainer(self, lrfWriter, parent): - parent.appendLrfTag(LrfTag("pstart", 0)) + parent.appendLrfTag(LrfTag('pstart', 0)) for content in self.contents: content.toLrfContainer(lrfWriter, parent) - parent.appendLrfTag(LrfTag("pend")) + parent.appendLrfTag(LrfTag('pend')) def toElement(self, sourceEncoding): - p = Element("P") + p = Element('P') appendTextElements(p, self.contents, sourceEncoding) return p @@ -1537,7 +1537,7 @@ class LrsTextTag(LrsContainer): self.append(text) def toLrfContainer(self, lrfWriter, parent): - if hasattr(self, "tagName"): + if hasattr(self, 'tagName'): tagName = self.tagName else: tagName = self.__class__.__name__ @@ -1547,10 +1547,10 @@ class LrsTextTag(LrsContainer): for content in self.contents: content.toLrfContainer(lrfWriter, parent) - parent.appendLrfTag(LrfTag(tagName + "End")) + parent.appendLrfTag(LrfTag(tagName + 'End')) def toElement(self, se): - if hasattr(self, "tagName"): + if hasattr(self, 'tagName'): tagName = self.tagName else: tagName = self.__class__.__name__ @@ -1601,7 +1601,7 @@ class DropCaps(LrsTextTag): for content in self.contents: content.toLrfContainer(lrfWriter, parent) - parent.appendLrfTag(LrfTag("DrawCharEnd")) + parent.appendLrfTag(LrfTag('DrawCharEnd')) class Button(LrsObject, LrsContainer): @@ -1616,22 +1616,22 @@ class Button(LrsObject, LrsContainer): for sub2 in sub1.contents: if isinstance(sub2, JumpTo): return (sub2.textBlock.objId, sub2.textBlock.parent.objId) - raise LrsError("%s has no PushButton or JumpTo subs"%self.__class__.__name__) + raise LrsError('%s has no PushButton or JumpTo subs'%self.__class__.__name__) def toLrf(self, lrfWriter): (refobj, refpage) = self.findJumpToRefs() # print "Button writing JumpTo refobj=", jumpto.refobj, ", and refpage=", jumpto.refpage - button = LrfObject("Button", self.objId) - button.appendLrfTag(LrfTag("buttonflags", 0x10)) # pushbutton - button.appendLrfTag(LrfTag("PushButtonStart")) - button.appendLrfTag(LrfTag("buttonactions")) - button.appendLrfTag(LrfTag("jumpto", (int(refpage), int(refobj)))) - button.append(LrfTag("endbuttonactions")) - button.appendLrfTag(LrfTag("PushButtonEnd")) + button = LrfObject('Button', self.objId) + button.appendLrfTag(LrfTag('buttonflags', 0x10)) # pushbutton + button.appendLrfTag(LrfTag('PushButtonStart')) + button.appendLrfTag(LrfTag('buttonactions')) + button.appendLrfTag(LrfTag('jumpto', (int(refpage), int(refobj)))) + button.append(LrfTag('endbuttonactions')) + button.appendLrfTag(LrfTag('PushButtonEnd')) lrfWriter.append(button) def toElement(self, se): - b = self.lrsObjectElement("Button") + b = self.lrsObjectElement('Button') for content in self.contents: b.append(content.toElement(se)) @@ -1649,7 +1649,7 @@ class PushButton(LrsContainer): LrsContainer.__init__(self, [JumpTo]) def toElement(self, se): - b = Element("PushButton") + b = Element('PushButton') for content in self.contents: b.append(content.toElement(se)) @@ -1667,7 +1667,7 @@ class JumpTo(LrsContainer): self.textBlock = textBlock def toElement(self, se): - return Element("JumpTo", refpage=str(self.textBlock.parent.objId), refobj=str(self.textBlock.objId)) + return Element('JumpTo', refpage=str(self.textBlock.parent.objId), refobj=str(self.textBlock.objId)) class Plot(LrsSimpleChar1, LrsContainer): @@ -1709,11 +1709,11 @@ class Plot(LrsSimpleChar1, LrsContainer): adj = self.adjustment if self.adjustment else 'bottom' params = (int(self.xsize), int(self.ysize), int(self.obj.objId), Plot.ADJUSTMENT_VALUES[adj]) - parent.appendLrfTag(LrfTag("Plot", params)) + parent.appendLrfTag(LrfTag('Plot', params)) class Text(LrsContainer): - """ A object that represents raw text. Does not have a toElement. """ + ''' A object that represents raw text. Does not have a toElement. ''' def __init__(self, text): LrsContainer.__init__(self, []) @@ -1725,25 +1725,25 @@ class Text(LrsContainer): def toLrfContainer(self, lrfWriter, parent): if self.text: if isinstance(self.text, bytes): - parent.appendLrfTag(LrfTag("rawtext", self.text)) + parent.appendLrfTag(LrfTag('rawtext', self.text)) else: - parent.appendLrfTag(LrfTag("textstring", self.text)) + parent.appendLrfTag(LrfTag('textstring', self.text)) class CR(LrsSimpleChar1, LrsContainer): - """ + ''' A line break (when appended to a Paragraph) or a paragraph break (when appended to a TextBlock). - """ + ''' def __init__(self): LrsContainer.__init__(self, []) def toElement(self, se): - return Element("CR") + return Element('CR') def toLrfContainer(self, lrfWriter, parent): - parent.appendLrfTag(LrfTag("CR")) + parent.appendLrfTag(LrfTag('CR')) class Italic(LrsSimpleChar1, LrsTextTag): @@ -1782,35 +1782,35 @@ class Space(LrsSimpleChar1, LrsContainer): if self.xsize == 0: return - return Element("Space", xsize=str(self.xsize)) + return Element('Space', xsize=str(self.xsize)) def toLrfContainer(self, lrfWriter, container): if self.xsize != 0: - container.appendLrfTag(LrfTag("Space", self.xsize)) + container.appendLrfTag(LrfTag('Space', self.xsize)) class Box(LrsSimpleChar1, LrsContainer): - """ + ''' Draw a box around text. Unfortunately, does not seem to do anything on the PRS-500. - """ + ''' - def __init__(self, linetype="solid"): + def __init__(self, linetype='solid'): LrsContainer.__init__(self, [Text, bytes, str]) if linetype not in LINE_TYPE_ENCODING: - raise LrsError(linetype + " is not a valid line type") + raise LrsError(linetype + ' is not a valid line type') self.linetype = linetype def toElement(self, se): - e = Element("Box", linetype=self.linetype) + e = Element('Box', linetype=self.linetype) appendTextElements(e, self.contents, se) return e def toLrfContainer(self, lrfWriter, container): - container.appendLrfTag(LrfTag("Box", self.linetype)) + container.appendLrfTag(LrfTag('Box', self.linetype)) for content in self.contents: content.toLrfContainer(lrfWriter, container) - container.appendLrfTag(LrfTag("BoxEnd")) + container.appendLrfTag(LrfTag('BoxEnd')) class Span(LrsSimpleChar1, LrsContainer): @@ -1825,18 +1825,18 @@ class Span(LrsSimpleChar1, LrsContainer): for attrname in attrs.keys(): if attrname not in TextStyle.defaults and \ attrname not in TextStyle.alsoAllow: - raise LrsError("setting %s not allowed on Span" % attrname) + raise LrsError('setting %s not allowed on Span' % attrname) self.attrs = attrs def findCurrentTextStyle(self): parent = self.parent while 1: - if parent is None or hasattr(parent, "currentTextStyle"): + if parent is None or hasattr(parent, 'currentTextStyle'): break parent = parent.parent if parent is None: - raise LrsError("no enclosing current TextStyle found") + raise LrsError('no enclosing current TextStyle found') return parent.currentTextStyle @@ -1896,7 +1896,7 @@ class EmpLine(LrsTextTag, LrsSimpleChar1): for content in self.contents: content.toLrfContainer(lrfWriter, parent) - parent.appendLrfTag(LrfTag(self.__class__.__name__ + "End")) + parent.appendLrfTag(LrfTag(self.__class__.__name__ + 'End')) def toElement(self, se): element = Element(self.__class__.__name__) @@ -1908,22 +1908,22 @@ class EmpLine(LrsTextTag, LrsSimpleChar1): class Bold(Span): - """ + ''' There is no known "bold" lrf tag. Use Span with a fontweight in LRF, but use the word Bold in the LRS. - """ + ''' def __init__(self, text=None): Span.__init__(self, text, fontweight=800) def toElement(self, se): - e = Element("Bold") + e = Element('Bold') appendTextElements(e, self.contents, se) return e class BlockSpace(LrsContainer): - """ Can be appended to a page to move the text point. """ + ''' Can be appended to a page to move the text point. ''' def __init__(self, xspace=0, yspace=0, x=0, y=0): LrsContainer.__init__(self, []) @@ -1936,28 +1936,28 @@ class BlockSpace(LrsContainer): def toLrfContainer(self, lrfWriter, container): if self.xspace != 0: - container.appendLrfTag(LrfTag("xspace", self.xspace)) + container.appendLrfTag(LrfTag('xspace', self.xspace)) if self.yspace != 0: - container.appendLrfTag(LrfTag("yspace", self.yspace)) + container.appendLrfTag(LrfTag('yspace', self.yspace)) def toElement(self, se): - element = Element("BlockSpace") + element = Element('BlockSpace') if self.xspace != 0: - element.attrib["xspace"] = str(self.xspace) + element.attrib['xspace'] = str(self.xspace) if self.yspace != 0: - element.attrib["yspace"] = str(self.yspace) + element.attrib['yspace'] = str(self.yspace) return element class CharButton(LrsSimpleChar1, LrsContainer): - """ + ''' Define the text and target of a CharButton. Must be passed a JumpButton that is the destination of the CharButton. Only text or SimpleChars can be appended to the CharButton. - """ + ''' def __init__(self, button, text=None): LrsContainer.__init__(self, [bytes, str, Text, LrsSimpleChar1]) @@ -1970,7 +1970,7 @@ class CharButton(LrsSimpleChar1, LrsContainer): def setButton(self, button): if not isinstance(button, (JumpButton, Button)): - raise LrsError("CharButton button must be a JumpButton or Button") + raise LrsError('CharButton button must be a JumpButton or Button') self.button = button @@ -1982,15 +1982,15 @@ class CharButton(LrsSimpleChar1, LrsContainer): return [self.button.objId] def toLrfContainer(self, lrfWriter, container): - container.appendLrfTag(LrfTag("CharButton", self.button.objId)) + container.appendLrfTag(LrfTag('CharButton', self.button.objId)) for content in self.contents: content.toLrfContainer(lrfWriter, container) - container.appendLrfTag(LrfTag("CharButtonEnd")) + container.appendLrfTag(LrfTag('CharButtonEnd')) def toElement(self, se): - cb = Element("CharButton", refobj=str(self.button.objId)) + cb = Element('CharButton', refobj=str(self.button.objId)) appendTextElements(cb, self.contents, se) return cb @@ -2005,10 +2005,10 @@ class Objects(LrsContainer): self.appendImage = self.appendImageBlock = self.append def getMethods(self): - return ["JumpButton", "appendJumpButton", "TextBlock", - "appendTextBlock", "Header", "appendHeader", - "Footer", "appendFooter", "ImageBlock", - "ImageStream", "appendImageStream", + return ['JumpButton', 'appendJumpButton', 'TextBlock', + 'appendTextBlock', 'Header', 'appendHeader', + 'Footer', 'appendFooter', 'ImageBlock', + 'ImageStream', 'appendImageStream', 'Image','appendImage', 'appendImageBlock'] def getSettings(self): @@ -2050,7 +2050,7 @@ class Objects(LrsContainer): return i def toElement(self, se): - o = Element("Objects") + o = Element('Objects') for content in self.contents: o.append(content.toElement(se)) @@ -2063,11 +2063,11 @@ class Objects(LrsContainer): class JumpButton(LrsObject, LrsContainer): - """ + ''' The target of a CharButton. Needs a parented TextBlock to jump to. Actually creates several elements in the XML. JumpButtons must be eventually appended to a Book (actually, an Object.) - """ + ''' def __init__(self, textBlock): LrsObject.__init__(self) @@ -2078,31 +2078,31 @@ class JumpButton(LrsObject, LrsContainer): self.textBlock = textBlock def toLrf(self, lrfWriter): - button = LrfObject("Button", self.objId) - button.appendLrfTag(LrfTag("buttonflags", 0x10)) # pushbutton - button.appendLrfTag(LrfTag("PushButtonStart")) - button.appendLrfTag(LrfTag("buttonactions")) - button.appendLrfTag(LrfTag("jumpto", + button = LrfObject('Button', self.objId) + button.appendLrfTag(LrfTag('buttonflags', 0x10)) # pushbutton + button.appendLrfTag(LrfTag('PushButtonStart')) + button.appendLrfTag(LrfTag('buttonactions')) + button.appendLrfTag(LrfTag('jumpto', (self.textBlock.parent.objId, self.textBlock.objId))) - button.append(LrfTag("endbuttonactions")) - button.appendLrfTag(LrfTag("PushButtonEnd")) + button.append(LrfTag('endbuttonactions')) + button.appendLrfTag(LrfTag('PushButtonEnd')) lrfWriter.append(button) def toElement(self, se): - b = self.lrsObjectElement("Button") - pb = SubElement(b, "PushButton") - SubElement(pb, "JumpTo", + b = self.lrsObjectElement('Button') + pb = SubElement(b, 'PushButton') + SubElement(pb, 'JumpTo', refpage=str(self.textBlock.parent.objId), refobj=str(self.textBlock.objId)) return b class RuledLine(LrsContainer, LrsAttributes, LrsObject): - """ A line. Default is 500 pixels long, 2 pixels wide. """ + ''' A line. Default is 500 pixels long, 2 pixels wide. ''' defaults = dict( - linelength="500", linetype="solid", linewidth="2", - linecolor="0x00000000") + linelength='500', linetype='solid', linewidth='2', + linecolor='0x00000000') def __init__(self, **settings): LrsContainer.__init__(self, []) @@ -2111,23 +2111,23 @@ class RuledLine(LrsContainer, LrsAttributes, LrsObject): def toLrfContainer(self, lrfWriter, container): a = self.attrs - container.appendLrfTag(LrfTag("RuledLine", - (a["linelength"], a["linetype"], a["linewidth"], a["linecolor"]))) + container.appendLrfTag(LrfTag('RuledLine', + (a['linelength'], a['linetype'], a['linewidth'], a['linecolor']))) def toElement(self, se): - return Element("RuledLine", self.attrs) + return Element('RuledLine', self.attrs) class HeaderOrFooter(LrsObject, LrsContainer, LrsAttributes): - """ + ''' Creates empty header or footer objects. Append PutObj objects to the header or footer to create the text. Note: it seems that adding multiple PutObjs to a header or footer only shows the last one. - """ - defaults = dict(framemode="square", layout="LrTb", framewidth="0", - framecolor="0x00000000", bgcolor="0xFF000000") + ''' + defaults = dict(framemode='square', layout='LrTb', framewidth='0', + framecolor='0x00000000', bgcolor='0xFF000000') def __init__(self, **settings): LrsObject.__init__(self) @@ -2155,7 +2155,7 @@ class HeaderOrFooter(LrsObject, LrsContainer, LrsAttributes): def toElement(self, se): name = self.__class__.__name__ - labelName = name.lower() + "label" + labelName = name.lower() + 'label' hd = self.lrsObjectElement(name, objlabel=labelName) hd.attrib.update(self.attrs) @@ -2174,8 +2174,8 @@ class Footer(HeaderOrFooter): class Canvas(LrsObject, LrsContainer, LrsAttributes): - defaults = dict(framemode="square", layout="LrTb", framewidth="0", - framecolor="0x00000000", bgcolor="0xFF000000", + defaults = dict(framemode='square', layout='LrTb', framewidth='0', + framecolor='0x00000000', bgcolor='0xFF000000', canvasheight=0, canvaswidth=0, blockrule='block-adjustable') def __init__(self, width, height, **settings): @@ -2192,7 +2192,7 @@ class Canvas(LrsObject, LrsContainer, LrsAttributes): self.append(PutObj(obj, x1, y1)) def toElement(self, source_encoding): - el = self.lrsObjectElement("Canvas", **self.settings) + el = self.lrsObjectElement('Canvas', **self.settings) for po in self.contents: el.append(po.toElement(source_encoding)) return el @@ -2201,7 +2201,7 @@ class Canvas(LrsObject, LrsContainer, LrsAttributes): self.toLrfContainer(lrfWriter, lrfWriter) def toLrfContainer(self, lrfWriter, container): - c = LrfObject("Canvas", self.objId) + c = LrfObject('Canvas', self.objId) c.appendTagDict(self.settings) stream = LrfTagStream(STREAM_COMPRESSED) for content in self.contents: @@ -2221,7 +2221,7 @@ class Canvas(LrsObject, LrsContainer, LrsAttributes): class PutObj(LrsContainer): - """ PutObj holds other objects that are drawn on a Canvas or Header. """ + ''' PutObj holds other objects that are drawn on a Canvas or Header. ''' def __init__(self, content, x1=0, y1=0): LrsContainer.__init__(self, [TextBlock, ImageBlock]) @@ -2237,21 +2237,21 @@ class PutObj(LrsContainer): parent.append(self.content) def toLrfContainer(self, lrfWriter, container): - container.appendLrfTag(LrfTag("PutObj", (self.x1, self.y1, + container.appendLrfTag(LrfTag('PutObj', (self.x1, self.y1, self.content.objId))) def toElement(self, se): - el = Element("PutObj", x1=str(self.x1), y1=str(self.y1), + el = Element('PutObj', x1=str(self.x1), y1=str(self.y1), refobj=str(self.content.objId)) return el class ImageStream(LrsObject, LrsContainer): - """ + ''' Embed an image file into an Lrf. - """ + ''' - VALID_ENCODINGS = ["JPEG", "GIF", "BMP", "PNG"] + VALID_ENCODINGS = ['JPEG', 'GIF', 'BMP', 'PNG'] def __init__(self, file=None, encoding=None, comment=None): LrsObject.__init__(self) @@ -2263,28 +2263,28 @@ class ImageStream(LrsObject, LrsContainer): if encoding is None: extension = os.path.splitext(file)[1] if not extension: - raise LrsError("file must have extension if encoding is not specified") + raise LrsError('file must have extension if encoding is not specified') extension = extension[1:].upper() - if extension == "JPG": - extension = "JPEG" + if extension == 'JPG': + extension = 'JPEG' encoding = extension else: encoding = encoding.upper() if encoding not in self.VALID_ENCODINGS: - raise LrsError("encoding or file extension not JPEG, GIF, BMP, or PNG") + raise LrsError('encoding or file extension not JPEG, GIF, BMP, or PNG') self.encoding = encoding def toLrf(self, lrfWriter): - with open(self.filename, "rb") as f: + with open(self.filename, 'rb') as f: imageData = f.read() - isObj = LrfObject("ImageStream", self.objId) + isObj = LrfObject('ImageStream', self.objId) if self.comment is not None: - isObj.appendLrfTag(LrfTag("comment", self.comment)) + isObj.appendLrfTag(LrfTag('comment', self.comment)) streamFlags = IMAGE_TYPE_ENCODING[self.encoding] stream = LrfStreamBase(streamFlags, imageData) @@ -2292,8 +2292,8 @@ class ImageStream(LrsObject, LrsContainer): lrfWriter.append(isObj) def toElement(self, se): - element = self.lrsObjectElement("ImageStream", - objlabel="imagestreamlabel", + element = self.lrsObjectElement('ImageStream', + objlabel='imagestreamlabel', encoding=self.encoding, file=self.filename) element.text = self.comment return element @@ -2323,29 +2323,29 @@ class Image(LrsObject, LrsContainer, LrsAttributes): return [self.objId, self.refstream.objId] def toElement(self, se): - element = self.lrsObjectElement("Image", **self.attrs) - element.set("refstream", str(self.refstream.objId)) - for name in ["x0", "y0", "x1", "y1", "xsize", "ysize"]: + element = self.lrsObjectElement('Image', **self.attrs) + element.set('refstream', str(self.refstream.objId)) + for name in ['x0', 'y0', 'x1', 'y1', 'xsize', 'ysize']: element.set(name, str(getattr(self, name))) return element def toLrf(self, lrfWriter): - ib = LrfObject("Image", self.objId) - ib.appendLrfTag(LrfTag("ImageRect", + ib = LrfObject('Image', self.objId) + ib.appendLrfTag(LrfTag('ImageRect', (self.x0, self.y0, self.x1, self.y1))) - ib.appendLrfTag(LrfTag("ImageSize", (self.xsize, self.ysize))) - ib.appendLrfTag(LrfTag("RefObjId", self.refstream.objId)) + ib.appendLrfTag(LrfTag('ImageSize', (self.xsize, self.ysize))) + ib.appendLrfTag(LrfTag('RefObjId', self.refstream.objId)) lrfWriter.append(ib) class ImageBlock(LrsObject, LrsContainer, LrsAttributes): - """ Create an image on a page. """ + ''' Create an image on a page. ''' # TODO: allow other block attributes defaults = BlockStyle.baseDefaults.copy() - def __init__(self, refstream, x0="0", y0="0", x1="600", y1="800", - xsize="600", ysize="800", + def __init__(self, refstream, x0='0', y0='0', x1='600', y1='800', + xsize='600', ysize='800', blockStyle=BlockStyle(blockrule='block-fixed'), alttext=None, **settings): LrsObject.__init__(self) @@ -2382,40 +2382,40 @@ class ImageBlock(LrsObject, LrsContainer, LrsAttributes): extraId = LrsObject.getNextObjId() - b = LrfObject("Block", self.objId) + b = LrfObject('Block', self.objId) if self.blockStyle is not None: - b.appendLrfTag(LrfTag("Link", self.blockStyle.objId)) + b.appendLrfTag(LrfTag('Link', self.blockStyle.objId)) b.appendTagDict(self.attrs) b.appendLrfTags( LrfTagStream(0, - [LrfTag("Link", extraId)]).getStreamTags(lrfWriter.getSourceEncoding())) + [LrfTag('Link', extraId)]).getStreamTags(lrfWriter.getSourceEncoding())) container.addLrfObject(b.objId) lrfWriter.append(b) - ib = LrfObject("Image", extraId) + ib = LrfObject('Image', extraId) - ib.appendLrfTag(LrfTag("ImageRect", + ib.appendLrfTag(LrfTag('ImageRect', (self.x0, self.y0, self.x1, self.y1))) - ib.appendLrfTag(LrfTag("ImageSize", (self.xsize, self.ysize))) - ib.appendLrfTag(LrfTag("RefObjId", self.refstream.objId)) + ib.appendLrfTag(LrfTag('ImageSize', (self.xsize, self.ysize))) + ib.appendLrfTag(LrfTag('RefObjId', self.refstream.objId)) if self.alttext: - ib.appendLrfTag("Comment", self.alttext) + ib.appendLrfTag('Comment', self.alttext) lrfWriter.append(ib) self.extraId = extraId def toElement(self, se): - element = self.lrsObjectElement("ImageBlock", **self.attrs) - element.set("refstream", str(self.refstream.objId)) - for name in ["x0", "y0", "x1", "y1", "xsize", "ysize"]: + element = self.lrsObjectElement('ImageBlock', **self.attrs) + element.set('refstream', str(self.refstream.objId)) + for name in ['x0', 'y0', 'x1', 'y1', 'xsize', 'ysize']: element.set(name, str(getattr(self, name))) element.text = self.alttext return element class Font(LrsContainer): - """ Allows a TrueType file to be embedded in an Lrf. """ + ''' Allows a TrueType file to be embedded in an Lrf. ''' def __init__(self, file=None, fontname=None, fontfilename=None, encoding=None): LrsContainer.__init__(self, []) @@ -2435,11 +2435,11 @@ class Font(LrsContainer): self.encoding = encoding def toLrf(self, lrfWriter): - font = LrfObject("Font", LrsObject.getNextObjId()) + font = LrfObject('Font', LrsObject.getNextObjId()) lrfWriter.registerFontId(font.objId) - font.appendLrfTag(LrfTag("FontFilename", + font.appendLrfTag(LrfTag('FontFilename', lrfWriter.toUnicode(self.truefile))) - font.appendLrfTag(LrfTag("FontFacename", + font.appendLrfTag(LrfTag('FontFacename', lrfWriter.toUnicode(self.fontname))) stream = LrfFileStream(STREAM_FORCE_COMPRESSED, self.truefile) @@ -2448,6 +2448,6 @@ class Font(LrsContainer): lrfWriter.append(font) def toElement(self, se): - element = Element("RegistFont", encoding="TTF", fontname=self.fontname, + element = Element('RegistFont', encoding='TTF', fontname=self.fontname, file=self.file, fontfilename=self.file) return element diff --git a/src/calibre/ebooks/lrf/tags.py b/src/calibre/ebooks/lrf/tags.py index 13c56fbf51..ffc53e45dd 100644 --- a/src/calibre/ebooks/lrf/tags.py +++ b/src/calibre/ebooks/lrf/tags.py @@ -10,18 +10,18 @@ from calibre.ebooks.lrf import LRFParseError class Tag: tags = { - 0x00 : (6, "*ObjectStart"), - 0x01 : (0, "*ObjectEnd"), - 0x02 : (4, "*ObjectInfoLink"), - 0x03 : (4, "*Link"), - 0x04 : (4, "*StreamSize"), - 0x05 : (0, "*StreamStart"), - 0x06 : (0, "*StreamEnd"), + 0x00 : (6, '*ObjectStart'), + 0x01 : (0, '*ObjectEnd'), + 0x02 : (4, '*ObjectInfoLink'), + 0x03 : (4, '*Link'), + 0x04 : (4, '*StreamSize'), + 0x05 : (0, '*StreamStart'), + 0x06 : (0, '*StreamEnd'), 0x07 : (4, None), 0x08 : (4, None), 0x09 : (4, None), 0x0A : (4, None), - 0x0B : ("type_one", "*ContainedObjectsList"), + 0x0B : ('type_one', '*ContainedObjectsList'), 0x0D : (2, None), 0x0E : (2, None), 0x11 : (2, None), @@ -29,7 +29,7 @@ class Tag: 0x13 : (2, None), 0x14 : (2, None), 0x15 : (2, None), - 0x16 : ("string", None), + 0x16 : ('string', None), 0x17 : (4, None), 0x18 : (4, None), 0x19 : (2, None), @@ -81,16 +81,16 @@ class Tag: 0x51 : (2, None), 0x52 : (2, None), 0x53 : (4, None), - 0x54 : (2, "*StreamFlags"), - 0x55 : ("string", None), + 0x54 : (2, '*StreamFlags'), + 0x55 : ('string', None), 0x56 : (2, None), 0x57 : (2, None), 0x58 : (2, None), - 0x59 : ("string", None), - 0x5A : ("string", None), + 0x59 : ('string', None), + 0x5A : ('string', None), 0x5B : (4, None), - 0x5C : ("type_one", None), - 0x5D : ("string", None), + 0x5C : ('type_one', None), + 0x5D : ('string', None), 0x5E : (2, None), 0x61 : (2, None), 0x62 : (0, None), @@ -112,16 +112,16 @@ class Tag: 0x75 : (2, None), 0x76 : (2, None), 0x77 : (2, None), - 0x78 : ("tag_78", None), + 0x78 : ('tag_78', None), 0x79 : (2, None), 0x7A : (2, None), 0x7B : (4, None), - 0x7C : (4, "*ParentPageTree"), + 0x7C : (4, '*ParentPageTree'), 0x81 : (0, None), 0x82 : (0, None), 0xA1 : (4, None), 0xA2 : (0, None), - 0xA5 : ("unknown", None), + 0xA5 : ('unknown', None), 0xA6 : (0, None), 0xA7 : (4, None), 0xA8 : (0, None), @@ -155,7 +155,7 @@ class Tag: 0xC8 : (2, None), 0xC9 : (0, None), 0xCA : (2, None), - 0xCB : ("unknown", None), + 0xCB : ('unknown', None), 0xCC : (2, None), 0xD1 : (12, None), 0xD2 : (0, None), @@ -186,11 +186,11 @@ class Tag: def __init__(self, stream): self.offset = stream.tell() - tag_id = struct.unpack("<BB", stream.read(2)) + tag_id = struct.unpack('<BB', stream.read(2)) if tag_id[1] != 0xF5: - raise LRFParseError("Bad tag ID %02X at %d"%(tag_id[1], self.offset)) + raise LRFParseError('Bad tag ID %02X at %d'%(tag_id[1], self.offset)) if tag_id[0] not in self.__class__.tags: - raise LRFParseError("Unknown tag ID: F5%02X" % tag_id[0]) + raise LRFParseError('Unknown tag ID: F5%02X' % tag_id[0]) self.id = 0xF500 + tag_id[0] @@ -202,59 +202,59 @@ class Tag: self.contents = stream.read(size) def __str__(self): - s = "Tag %04X " % self.id + s = 'Tag %04X ' % self.id if self.name: s += self.name - s += f" at {self.offset:08X}, contents: {repr(self.contents)}" + s += f' at {self.offset:08X}, contents: {repr(self.contents)}' return s @property def byte(self): if len(self.contents) != 1: - raise LRFParseError("Bad parameter for tag ID: %04X" % self.id) - return struct.unpack("<B", self.contents)[0] + raise LRFParseError('Bad parameter for tag ID: %04X' % self.id) + return struct.unpack('<B', self.contents)[0] @property def word(self): if len(self.contents) != 2: - raise LRFParseError("Bad parameter for tag ID: %04X" % self.id) - return struct.unpack("<H", self.contents)[0] + raise LRFParseError('Bad parameter for tag ID: %04X' % self.id) + return struct.unpack('<H', self.contents)[0] @property def sword(self): if len(self.contents) != 2: - raise LRFParseError("Bad parameter for tag ID: %04X" % self.id) - return struct.unpack("<h", self.contents)[0] + raise LRFParseError('Bad parameter for tag ID: %04X' % self.id) + return struct.unpack('<h', self.contents)[0] @property def dword(self): if len(self.contents) != 4: - raise LRFParseError("Bad parameter for tag ID: %04X" % self.id) - return struct.unpack("<I", self.contents)[0] + raise LRFParseError('Bad parameter for tag ID: %04X' % self.id) + return struct.unpack('<I', self.contents)[0] def dummy_parser(self, stream): - raise LRFParseError("Unknown tag at %08X" % stream.tell()) + raise LRFParseError('Unknown tag at %08X' % stream.tell()) @classmethod def string_parser(self, stream): - size = struct.unpack("<H", stream.read(2))[0] - return str(stream.read(size), "utf_16") + size = struct.unpack('<H', stream.read(2))[0] + return str(stream.read(size), 'utf_16') def type_one_parser(self, stream): - cnt = struct.unpack("<H", stream.read(2))[0] + cnt = struct.unpack('<H', stream.read(2))[0] res = [] while cnt > 0: - res.append(struct.unpack("<I", stream.read(4))[0]) + res.append(struct.unpack('<I', stream.read(4))[0]) cnt -= 1 return res def tag_78_parser(self, stream): pos = stream.tell() res = [] - res.append(struct.unpack("<I", stream.read(4))[0]) + res.append(struct.unpack('<I', stream.read(4))[0]) tag = Tag(stream) if tag.id != 0xF516: - raise LRFParseError("Bad tag 78 at %08X" % pos) + raise LRFParseError('Bad tag 78 at %08X' % pos) res.append(tag.contents) - res.append(struct.unpack("<H", stream.read(2))[0]) + res.append(struct.unpack('<H', stream.read(2))[0]) return res diff --git a/src/calibre/ebooks/metadata/__init__.py b/src/calibre/ebooks/metadata/__init__.py index 5d82b0a666..8e62be2ce7 100644 --- a/src/calibre/ebooks/metadata/__init__.py +++ b/src/calibre/ebooks/metadata/__init__.py @@ -5,9 +5,9 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __docformat__ = 'restructuredtext en' -""" +''' Provides abstraction for metadata reading.writing from a variety of ebook formats. -""" +''' import os import re import sys @@ -228,7 +228,7 @@ def title_sort(title, order=None, lang=None): coding = list(zip( [1000,900,500,400,100,90,50,40,10,9,5,4,1], -["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"] +['M','CM','D','CD','C','XC','L','XL','X','IX','V','IV','I'] )) diff --git a/src/calibre/ebooks/metadata/book/base.py b/src/calibre/ebooks/metadata/book/base.py index 1e42a10426..b3f67c535e 100644 --- a/src/calibre/ebooks/metadata/book/base.py +++ b/src/calibre/ebooks/metadata/book/base.py @@ -25,7 +25,7 @@ SIMPLE_SET = frozenset(SIMPLE_GET - {'identifiers'}) def human_readable(size, precision=2): - """ Convert a size in bytes into megabytes """ + ''' Convert a size in bytes into megabytes ''' ans = size/(1024*1024) if ans < 0.1: return '<0.1 MB' @@ -448,7 +448,7 @@ class Metadata: if field is not None: if not field.startswith('#'): raise AttributeError( - 'Custom field name %s must begin with \'#\''%repr(field)) + "Custom field name %s must begin with '#'"%repr(field)) if metadata is None: traceback.print_stack() return diff --git a/src/calibre/ebooks/metadata/book/json_codec.py b/src/calibre/ebooks/metadata/book/json_codec.py index 093ec68147..99c89dc3ac 100644 --- a/src/calibre/ebooks/metadata/book/json_codec.py +++ b/src/calibre/ebooks/metadata/book/json_codec.py @@ -21,7 +21,7 @@ from polyglot.builtins import as_bytes, iteritems, itervalues def string_to_datetime(src): from calibre.utils.iso8601 import parse_iso8601 - if src != "None": + if src != 'None': try: return parse_iso8601(src) except Exception: @@ -32,13 +32,13 @@ def string_to_datetime(src): def datetime_to_string(dateval): from calibre.utils.date import UNDEFINED_DATE, isoformat, local_tz if dateval is None: - return "None" + return 'None' if not isinstance(dateval, datetime): dateval = datetime.combine(dateval, time()) if hasattr(dateval, 'tzinfo') and dateval.tzinfo is None: dateval = dateval.replace(tzinfo=local_tz) if dateval <= UNDEFINED_DATE: - return "None" + return 'None' return isoformat(dateval) diff --git a/src/calibre/ebooks/metadata/book/render.py b/src/calibre/ebooks/metadata/book/render.py index 4418213291..b2e06ad14b 100644 --- a/src/calibre/ebooks/metadata/book/render.py +++ b/src/calibre/ebooks/metadata/book/render.py @@ -355,7 +355,7 @@ def mi_to_html( val = _( '%(sidx)s of <a href="%(href)s" title="%(tt)s">' '<span class="%(cls)s">%(series)s</span></a>') % dict( - sidx=fmt_sidx(sidx, use_roman=use_roman_numbers), cls="series_name", + sidx=fmt_sidx(sidx, use_roman=use_roman_numbers), cls='series_name', series=p(series), href=search_action_with_data(st, series, book_id, field), tt=p(_('Click to see books in this series'))) val += add_other_links(field, series) diff --git a/src/calibre/ebooks/metadata/epub.py b/src/calibre/ebooks/metadata/epub.py index 70cbd08b8c..872f72bcd2 100644 --- a/src/calibre/ebooks/metadata/epub.py +++ b/src/calibre/ebooks/metadata/epub.py @@ -42,14 +42,14 @@ class Container(dict): return container = safe_xml_fromstring(stream.read()) if container.get('version', None) != '1.0': - raise EPubException("unsupported version of OCF") + raise EPubException('unsupported version of OCF') rootfiles = container.xpath('./*[local-name()="rootfiles"]') if not rootfiles: - raise EPubException("<rootfiles/> element missing") + raise EPubException('<rootfiles/> element missing') for rootfile in rootfiles[0].xpath('./*[local-name()="rootfile"]'): mt, fp = rootfile.get('media-type'), rootfile.get('full-path') if not mt or not fp: - raise EPubException("<rootfile/> element malformed") + raise EPubException('<rootfile/> element malformed') if file_exists and not file_exists(fp): # Some Kobo epubs have multiple rootfile entries, but only one @@ -98,16 +98,16 @@ class OCFReader(OCF): if mimetype != OCF.MIMETYPE: print('WARNING: Invalid mimetype declaration', mimetype) except: - print('WARNING: Epub doesn\'t contain a valid mimetype declaration') + print("WARNING: Epub doesn't contain a valid mimetype declaration") try: with closing(self.open(OCF.CONTAINER_PATH)) as f: self.container = Container(f, self.exists) except KeyError: - raise EPubException("missing OCF container.xml file") + raise EPubException('missing OCF container.xml file') self.opf_path = self.container[OPF.MIMETYPE] if not self.opf_path: - raise EPubException("missing OPF package file entry in container") + raise EPubException('missing OPF package file entry in container') self._opf_cached = self._encryption_meta_cached = None @property @@ -117,7 +117,7 @@ class OCFReader(OCF): with closing(self.open(self.opf_path)) as f: self._opf_cached = OPF(f, self.root, populate_spine=False) except KeyError: - raise EPubException("missing OPF package file") + raise EPubException('missing OPF package file') return self._opf_cached @property @@ -150,7 +150,7 @@ class OCFZipReader(OCFReader): try: self.archive = ZipFile(stream, mode=mode) except BadZipfile: - raise EPubException("not a ZIP .epub OCF container") + raise EPubException('not a ZIP .epub OCF container') self.root = root if self.root is None: name = getattr(stream, 'name', False) @@ -219,12 +219,12 @@ def render_cover(cpage, zf, reader=None): # In the case of manga, the first spine item may be an image # already, so treat it as a raster cover. file_format = what_image_type(cpage) - if file_format == "jpeg": + if file_format == 'jpeg': # Only JPEG is allowed since elsewhere we assume raster covers # are JPEG. In principle we could convert other image formats # but this is already an out-of-spec case that happens to # arise in books from some stores. - with open(cpage, "rb") as source: + with open(cpage, 'rb') as source: return source.read() return render_html_svg_workaround(cpage, default_log, root=tdir) @@ -260,7 +260,7 @@ def get_cover(raster_cover, first_spine_item, reader): def get_metadata(stream, extract_cover=True): - """ Return metadata as a :class:`Metadata` object """ + ''' Return metadata as a :class:`Metadata` object ''' stream.seek(0) reader = get_zip_reader(stream) opfbytes = reader.read_bytes(reader.opf_path) diff --git a/src/calibre/ebooks/metadata/ereader.py b/src/calibre/ebooks/metadata/ereader.py index 2ffe6685ab..20aa4743ce 100644 --- a/src/calibre/ebooks/metadata/ereader.py +++ b/src/calibre/ebooks/metadata/ereader.py @@ -28,9 +28,9 @@ def get_cover(pheader, eheader): def get_metadata(stream, extract_cover=True): - """ + ''' Return metadata as a L{MetaInfo} object - """ + ''' mi = MetaInformation(None, [_('Unknown')]) stream.seek(0) diff --git a/src/calibre/ebooks/metadata/fb2.py b/src/calibre/ebooks/metadata/fb2.py index bf0b17bc70..6eac38edb1 100644 --- a/src/calibre/ebooks/metadata/fb2.py +++ b/src/calibre/ebooks/metadata/fb2.py @@ -179,7 +179,7 @@ def _parse_authors(root, ctx): def _parse_author(elm_author, ctx): - """ Returns a list of display author and sortable author""" + ''' Returns a list of display author and sortable author''' xp_templ = 'normalize-space(fb:%s/text())' diff --git a/src/calibre/ebooks/metadata/imp.py b/src/calibre/ebooks/metadata/imp.py index e244eb2621..46de408ef2 100644 --- a/src/calibre/ebooks/metadata/imp.py +++ b/src/calibre/ebooks/metadata/imp.py @@ -10,13 +10,13 @@ MAGIC = (b'\x00\x01BOOKDOUG', b'\x00\x02BOOKDOUG') def get_metadata(stream): - """ Return metadata as a L{MetaInfo} object """ + ''' Return metadata as a L{MetaInfo} object ''' title = 'Unknown' mi = MetaInformation(title, ['Unknown']) stream.seek(0) try: if stream.read(10) not in MAGIC: - print('Couldn\'t read IMP header from file', file=sys.stderr) + print("Couldn't read IMP header from file", file=sys.stderr) return mi def cString(skip=0): @@ -42,6 +42,6 @@ def get_metadata(stream): if category: mi.category = category except Exception as err: - msg = 'Couldn\'t read metadata from imp: %s with error %s'%(mi.title, str(err)) + msg = "Couldn't read metadata from imp: %s with error %s"%(mi.title, str(err)) print(msg.encode('utf8'), file=sys.stderr) return mi diff --git a/src/calibre/ebooks/metadata/kdl.py b/src/calibre/ebooks/metadata/kdl.py index 37acbb60b3..77d171eeff 100644 --- a/src/calibre/ebooks/metadata/kdl.py +++ b/src/calibre/ebooks/metadata/kdl.py @@ -18,7 +18,7 @@ from polyglot.builtins import codepoint_to_chr from polyglot.urllib import parse_qs, quote_plus URL = \ -"http://ww2.kdl.org/libcat/WhatsNext.asp?AuthorLastName={0}&AuthorFirstName=&SeriesName=&BookTitle={1}&CategoryID=0&cmdSearch=Search&Search=1&grouping=" +'http://ww2.kdl.org/libcat/WhatsNext.asp?AuthorLastName={0}&AuthorFirstName=&SeriesName=&BookTitle={1}&CategoryID=0&cmdSearch=Search&Search=1&grouping=' _ignore_starts = '\'"'+''.join(codepoint_to_chr(x) for x in list(range(0x2018, 0x201e))+[0x2032, 0x2033]) diff --git a/src/calibre/ebooks/metadata/kfx.py b/src/calibre/ebooks/metadata/kfx.py index 992cd53048..d9f900cead 100644 --- a/src/calibre/ebooks/metadata/kfx.py +++ b/src/calibre/ebooks/metadata/kfx.py @@ -50,14 +50,14 @@ PROP_METADATA_VALUE = b'P307' PROP_IMAGE = b'P417' METADATA_PROPERTIES = { - b'P10' : "languages", - b'P153': "title", - b'P154': "description", - b'P222': "author", - b'P232': "publisher", + b'P10' : 'languages', + b'P153': 'title', + b'P154': 'description', + b'P222': 'author', + b'P232': 'publisher', } -COVER_KEY = "cover_image_base64" +COVER_KEY = 'cover_image_base64' def hexs(string, sep=' '): @@ -248,7 +248,7 @@ class PackedIon(PackedData): def property_name(property_number): # This should be changed to translate property numbers to the proper # strings using a symbol table - return b"P%d" % property_number + return b'P%d' % property_number def extract_metadata(container_data): diff --git a/src/calibre/ebooks/metadata/mobi.py b/src/calibre/ebooks/metadata/mobi.py index dbba75145a..d651b167a1 100644 --- a/src/calibre/ebooks/metadata/mobi.py +++ b/src/calibre/ebooks/metadata/mobi.py @@ -53,20 +53,20 @@ class StreamSlicer: start, stop = stop, start size = stop - start if size <= 0: - return b"" + return b'' stream.seek(base + start) data = stream.read(size) if stride != 1: data = data[::stride] return data - raise TypeError("stream indices must be integers") + raise TypeError('stream indices must be integers') def __setitem__(self, key, value): stream = self._stream base = self.start if isinstance(key, numbers.Integral): if len(value) != 1: - raise ValueError("key and value lengths must match") + raise ValueError('key and value lengths must match') stream.seek(base + key) return stream.write(value) if isinstance(key, slice): @@ -77,10 +77,10 @@ class StreamSlicer: if stride != 1: value = value[::stride] if len(value) != size: - raise ValueError("key and value lengths must match") + raise ValueError('key and value lengths must match') stream.seek(base + start) return stream.write(value) - raise TypeError("stream indices must be integers") + raise TypeError('stream indices must be integers') def update(self, data_blocks): # Rewrite the stream @@ -103,7 +103,7 @@ class MetadataUpdater: data = self.data = StreamSlicer(stream) self.type = data[60:68] - if self.type != b"BOOKMOBI": + if self.type != b'BOOKMOBI': return self.nrecs, = unpack('>H', data[76:78]) @@ -233,7 +233,7 @@ class MetadataUpdater: mobi_header_length, = unpack('>L', self.record0[0x14:0x18]) if mobi_header_length == 0xe4: # Patch mobi_header_length to 0xE8 - self.record0[0x17] = b"\xe8" + self.record0[0x17] = b'\xe8' self.record0[0xf4:0xf8] = pack('>L', 0xFFFFFFFF) mobi_header_length = 0xe8 @@ -284,9 +284,9 @@ class MetadataUpdater: result='' while src: s,src = src[:length],src[length:] - hexa = ' '.join(["%02X"%ord(x) for x in s]) + hexa = ' '.join(['%02X'%ord(x) for x in s]) s = s.translate(FILTER) - result += "%04X %-*s %s\n" % (N, length*3, hexa, s) + result += '%04X %-*s %s\n' % (N, length*3, hexa, s) N+=length print(result) @@ -307,11 +307,11 @@ class MetadataUpdater: def dump_pdbrecords(self): # Diagnostic - print("MetadataUpdater.dump_pdbrecords()") - print("%10s %10s %10s" % ("offset","flags","val")) + print('MetadataUpdater.dump_pdbrecords()') + print('%10s %10s %10s' % ('offset','flags','val')) for i in range(len(self.pdbrecords)): pdbrecord = self.pdbrecords[i] - print(f"{pdbrecord[0]:10X} {pdbrecord[1]:10X} {pdbrecord[2]:10X}") + print(f'{pdbrecord[0]:10X} {pdbrecord[1]:10X} {pdbrecord[2]:10X}') def record(self, n): if n >= self.nrecs: @@ -331,7 +331,7 @@ class MetadataUpdater: if rec[0] in self.original_exth_records: self.original_exth_records.pop(rec[0]) - if self.type != b"BOOKMOBI": + if self.type != b'BOOKMOBI': raise MobiError("Setting metadata only supported for MOBI files of type 'BOOK'.\n" "\tThis is a %r file of type %r" % (self.type[0:4], self.type[4:8])) @@ -406,7 +406,7 @@ class MetadataUpdater: # Add a 112 record with actual UUID if getattr(mi, 'uuid', None): update_exth_record((112, - ("calibre:%s" % mi.uuid).encode(self.codec, 'replace'))) + ('calibre:%s' % mi.uuid).encode(self.codec, 'replace'))) if 503 in self.original_exth_records: update_exth_record((503, mi.title.encode(self.codec, 'replace'))) diff --git a/src/calibre/ebooks/metadata/odt.py b/src/calibre/ebooks/metadata/odt.py index 932a088f05..1245a5775e 100644 --- a/src/calibre/ebooks/metadata/odt.py +++ b/src/calibre/ebooks/metadata/odt.py @@ -171,7 +171,7 @@ def set_metadata(stream, mi): # print(raw.decode('utf-8')) stream.seek(os.SEEK_SET) - safe_replace(stream, "meta.xml", io.BytesIO(raw)) + safe_replace(stream, 'meta.xml', io.BytesIO(raw)) def _set_metadata(raw, mi): diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py index ac8216ed40..6232cce9d4 100644 --- a/src/calibre/ebooks/metadata/opf2.py +++ b/src/calibre/ebooks/metadata/opf2.py @@ -567,8 +567,8 @@ def dump_dict(cats): skipkeys=True) XPATH_NS = { - 'dc': "http://purl.org/dc/elements/1.1/", - 'opf': "http://www.idpf.org/2007/opf", + 'dc': 'http://purl.org/dc/elements/1.1/', + 'opf': 'http://www.idpf.org/2007/opf', 're' : 'http://exslt.org/regular-expressions' } XPath = functools.partial(etree.XPath, namespaces=XPATH_NS) @@ -578,9 +578,9 @@ class OPF: # {{{ MIMETYPE = 'application/oebps-package+xml' NAMESPACES = { - None: "http://www.idpf.org/2007/opf", - 'dc': "http://purl.org/dc/elements/1.1/", - 'opf': "http://www.idpf.org/2007/opf", + None: 'http://www.idpf.org/2007/opf', + 'dc': 'http://purl.org/dc/elements/1.1/', + 'opf': 'http://www.idpf.org/2007/opf', } META = '{%s}meta' % NAMESPACES['opf'] CONTENT = XPath('self::*[re:match(name(), "meta$", "i")]/@content') @@ -1841,7 +1841,7 @@ def suite(): def testWriting(self): for test in [('title', 'New & Title'), ('authors', ['One', 'Two']), - ('author_sort', "Kitchen"), ('tags', ['Three']), + ('author_sort', 'Kitchen'), ('tags', ['Three']), ('isbn', 'a'), ('rating', 3), ('series_index', 1), ('title_sort', 'ts')]: setattr(self.opf, *test) diff --git a/src/calibre/ebooks/metadata/opf3.py b/src/calibre/ebooks/metadata/opf3.py index b4619fbb60..a5330a46ba 100644 --- a/src/calibre/ebooks/metadata/opf3.py +++ b/src/calibre/ebooks/metadata/opf3.py @@ -135,7 +135,7 @@ def items_with_property(root, q, prefixes=None): if prefixes is None: prefixes = read_prefixes(root) q = expand_prefix(q, known_prefixes).lower() - for item in XPath("./opf:manifest/opf:item[@properties]")(root): + for item in XPath('./opf:manifest/opf:item[@properties]')(root): for prop in (item.get('properties') or '').lower().split(): prop = expand_prefix(prop, prefixes) if prop == q: diff --git a/src/calibre/ebooks/metadata/pdb.py b/src/calibre/ebooks/metadata/pdb.py index 783cd77bbe..15b68c31ce 100644 --- a/src/calibre/ebooks/metadata/pdb.py +++ b/src/calibre/ebooks/metadata/pdb.py @@ -31,9 +31,9 @@ MWRITER = { def get_metadata(stream, extract_cover=True): - """ + ''' Return metadata as a L{MetaInfo} object - """ + ''' pheader = PdbHeaderReader(stream) diff --git a/src/calibre/ebooks/metadata/pml.py b/src/calibre/ebooks/metadata/pml.py index db33a946d5..0fa91dc071 100644 --- a/src/calibre/ebooks/metadata/pml.py +++ b/src/calibre/ebooks/metadata/pml.py @@ -18,7 +18,7 @@ from calibre.utils.zipfile import ZipFile def get_metadata(stream, extract_cover=True): - """ Return metadata as a L{MetaInfo} object """ + ''' Return metadata as a L{MetaInfo} object ''' mi = MetaInformation(_('Unknown'), [_('Unknown')]) stream.seek(0) diff --git a/src/calibre/ebooks/metadata/rb.py b/src/calibre/ebooks/metadata/rb.py index b19a995346..7fbbbed075 100644 --- a/src/calibre/ebooks/metadata/rb.py +++ b/src/calibre/ebooks/metadata/rb.py @@ -12,13 +12,13 @@ MAGIC = b'\xb0\x0c\xb0\x0c\x02\x00NUVO\x00\x00\x00\x00' def get_metadata(stream): - """ Return metadata as a L{MetaInfo} object """ + ''' Return metadata as a L{MetaInfo} object ''' title = 'Unknown' mi = MetaInformation(title, ['Unknown']) stream.seek(0) try: if not stream.read(14) == MAGIC: - print('Couldn\'t read RB header from file', file=sys.stderr) + print("Couldn't read RB header from file", file=sys.stderr) return mi stream.read(10) @@ -34,7 +34,7 @@ def get_metadata(stream): if flag == 2: break else: - print('Couldn\'t find INFO from RB file', file=sys.stderr) + print("Couldn't find INFO from RB file", file=sys.stderr) return mi stream.seek(offset) @@ -48,7 +48,7 @@ def get_metadata(stream): elif key.strip() == 'AUTHOR': mi.authors = string_to_authors(value) except Exception as err: - msg = 'Couldn\'t read metadata from rb: %s with error %s'%(mi.title, str(err)) + msg = "Couldn't read metadata from rb: %s with error %s"%(mi.title, str(err)) prints(msg, file=sys.stderr) raise return mi diff --git a/src/calibre/ebooks/metadata/rtf.py b/src/calibre/ebooks/metadata/rtf.py index 07af613fa2..3a4c5b1e1c 100644 --- a/src/calibre/ebooks/metadata/rtf.py +++ b/src/calibre/ebooks/metadata/rtf.py @@ -1,9 +1,9 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2008, Kovid Goyal <kovid at kovidgoyal.net> -""" +''' Edit metadata in RTF files. -""" +''' import codecs import re @@ -20,15 +20,15 @@ publisher_pat = re.compile(br'\{\\info.*?\{\\manager(.*?)(?<!\\)\}', re.DOTALL) def get_document_info(stream): - """ + ''' Extract the \\info block from an RTF file. Return the info block as a string and the position in the file at which it starts. @param stream: File like object pointing to the RTF file. - """ + ''' block_size = 4096 stream.seek(0) - found, block = False, b"" + found, block = False, b'' while not found: prefix = block[-6:] block = prefix + stream.read(block_size) @@ -109,9 +109,9 @@ def decode(raw, codec): def get_metadata(stream): - """ + ''' Return metadata as a L{MetaInfo} object - """ + ''' stream.seek(0) if stream.read(5) != br'{\rtf': return MetaInformation(_('Unknown')) diff --git a/src/calibre/ebooks/metadata/snb.py b/src/calibre/ebooks/metadata/snb.py index 3c5593b3c5..d7c0a330cd 100644 --- a/src/calibre/ebooks/metadata/snb.py +++ b/src/calibre/ebooks/metadata/snb.py @@ -13,7 +13,7 @@ from calibre.utils.xml_parse import safe_xml_fromstring def get_metadata(stream, extract_cover=True): - """ Return metadata as a L{MetaInfo} object """ + ''' Return metadata as a L{MetaInfo} object ''' mi = MetaInformation(_('Unknown'), [_('Unknown')]) snbFile = SNBFile() diff --git a/src/calibre/ebooks/metadata/sources/amazon.py b/src/calibre/ebooks/metadata/sources/amazon.py index ad7450923f..1becef626e 100644 --- a/src/calibre/ebooks/metadata/sources/amazon.py +++ b/src/calibre/ebooks/metadata/sources/amazon.py @@ -910,7 +910,7 @@ class Worker(Thread): # Get details {{{ seen.add(lraw) return ans - def parse_cover(self, root, raw=b""): + def parse_cover(self, root, raw=b''): # Look for the image URL in javascript, using the first image in the # image gallery as the cover import json @@ -919,7 +919,7 @@ class Worker(Thread): # Get details {{{ m = imgpat.search(script.text or '') if m is not None: return m.group(1) - imgpat = re.compile(r"""'imageGalleryData'\s*:\s*(\[\s*{.+])""") + imgpat = re.compile(r''''imageGalleryData'\s*:\s*(\[\s*{.+])''') for script in root.xpath('//script'): m = imgpat.search(script.text or '') if m is not None: @@ -1131,7 +1131,7 @@ class Amazon(Source): options = ( Option('domain', 'choices', 'com', _('Amazon country website to use:'), _('Metadata from Amazon will be fetched using this ' - 'country\'s Amazon website.'), choices=AMAZON_DOMAINS), + "country's Amazon website."), choices=AMAZON_DOMAINS), Option('server', 'choices', 'auto', _('Server to get data from:'), _( 'Amazon has started blocking attempts to download' @@ -1219,7 +1219,7 @@ class Amazon(Source): self.set_amazon_id_touched_fields() def set_amazon_id_touched_fields(self): - ident_name = "identifier:amazon" + ident_name = 'identifier:amazon' if self.domain != 'com': ident_name += '_' + self.domain tf = [x for x in self.touched_fields if not @@ -1880,7 +1880,7 @@ def manual_tests(domain, **kw): # {{{ ), ( {'identifiers': {'isbn': '2221116798'}}, - [title_test('L\'étrange voyage de Monsieur Daldry', + [title_test("L'étrange voyage de Monsieur Daldry", exact=True), authors_test(['Marc Levy']) ] diff --git a/src/calibre/ebooks/metadata/sources/base.py b/src/calibre/ebooks/metadata/sources/base.py index 62148400f8..5bf5a19e98 100644 --- a/src/calibre/ebooks/metadata/sources/base.py +++ b/src/calibre/ebooks/metadata/sources/base.py @@ -26,8 +26,8 @@ def create_log(ostream=None): # Comparing Metadata objects for relevance {{{ -words = ("the", "a", "an", "of", "and") -prefix_pat = re.compile(r'^(%s)\s+'%("|".join(words))) +words = ('the', 'a', 'an', 'of', 'and') +prefix_pat = re.compile(r'^(%s)\s+'%('|'.join(words))) trailing_paren_pat = re.compile(r'\(.*\)$') whitespace_pat = re.compile(r'\s+') diff --git a/src/calibre/ebooks/metadata/sources/edelweiss.py b/src/calibre/ebooks/metadata/sources/edelweiss.py index 0326e22995..94719d0765 100644 --- a/src/calibre/ebooks/metadata/sources/edelweiss.py +++ b/src/calibre/ebooks/metadata/sources/edelweiss.py @@ -384,8 +384,8 @@ if __name__ == '__main__': from calibre.ebooks.metadata.sources.test import authors_test, comments_test, pubdate_test, test_identify_plugin, title_test tests = [ ( # A title and author search - {'title': 'The Husband\'s Secret', 'authors':['Liane Moriarty']}, - [title_test('The Husband\'s Secret', exact=True), + {'title': "The Husband's Secret", 'authors':['Liane Moriarty']}, + [title_test("The Husband's Secret", exact=True), authors_test(['Liane Moriarty'])] ), @@ -398,7 +398,7 @@ if __name__ == '__main__': # Multiple authors and two part title and no general description ({'identifiers':{'edelweiss':'0321180607'}}, [title_test( - "XQuery From the Experts: A Guide to the W3C XML Query Language" + 'XQuery From the Experts: A Guide to the W3C XML Query Language' , exact=True), authors_test([ 'Howard Katz', 'Don Chamberlin', 'Denise Draper', 'Mary Fernandez', 'Michael Kay', 'Jonathan Robie', 'Michael Rys', 'Jerome Simeon', diff --git a/src/calibre/ebooks/metadata/sources/test.py b/src/calibre/ebooks/metadata/sources/test.py index 307ad6f065..b6cea338a5 100644 --- a/src/calibre/ebooks/metadata/sources/test.py +++ b/src/calibre/ebooks/metadata/sources/test.py @@ -28,7 +28,7 @@ def isbn_test(isbn): misbn = check_isbn(mi.isbn) if misbn and misbn == isbn_: return True - prints('ISBN test failed. Expected: \'%s\' found \'%s\''%(isbn_, misbn)) + prints("ISBN test failed. Expected: '%s' found '%s'"%(isbn_, misbn)) return False return test @@ -43,7 +43,7 @@ def title_test(title, exact=False): if (exact and mt == title) or \ (not exact and title in mt): return True - prints('Title test failed. Expected: \'%s\' found \'%s\''%(title, mt)) + prints("Title test failed. Expected: '%s' found '%s'"%(title, mt)) return False return test @@ -70,7 +70,7 @@ def authors_test(authors, subset=False): return True if au == authors: return True - prints('Author test failed. Expected: \'%s\' found \'%s\''%(authors, au)) + prints("Author test failed. Expected: '%s' found '%s'"%(authors, au)) return False return test @@ -83,7 +83,7 @@ def tags_test(tags): t = {x.lower() for x in mi.tags} if t == tags: return True - prints('Tags test failed. Expected: \'%s\' found \'%s\''%(tags, t)) + prints("Tags test failed. Expected: '%s' found '%s'"%(tags, t)) return False return test @@ -97,10 +97,10 @@ def series_test(series, series_index): if (ms == series) and (series_index == mi.series_index): return True if mi.series: - prints('Series test failed. Expected: \'%s [%d]\' found \'%s[%d]\''% + prints("Series test failed. Expected: '%s [%d]' found '%s[%d]'"% (series, series_index, ms, mi.series_index)) else: - prints('Series test failed. Expected: \'%s [%d]\' found no series'% + prints("Series test failed. Expected: '%s [%d]' found no series"% (series, series_index)) return False diff --git a/src/calibre/ebooks/metadata/toc.py b/src/calibre/ebooks/metadata/toc.py index 80956e3ac7..e3edb062a1 100644 --- a/src/calibre/ebooks/metadata/toc.py +++ b/src/calibre/ebooks/metadata/toc.py @@ -19,8 +19,8 @@ from calibre.utils.cleantext import clean_xml_chars from calibre.utils.xml_parse import safe_xml_fromstring from polyglot.urllib import unquote, urlparse -NCX_NS = "http://www.daisy.org/z3986/2005/ncx/" -CALIBRE_NS = "http://calibre.kovidgoyal.net/2009/metadata" +NCX_NS = 'http://www.daisy.org/z3986/2005/ncx/' +CALIBRE_NS = 'http://calibre.kovidgoyal.net/2009/metadata' NSMAP = {None: NCX_NS, 'calibre':CALIBRE_NS} E = ElementMaker(namespace=NCX_NS, nsmap=NSMAP) C = ElementMaker(namespace=CALIBRE_NS, nsmap=NSMAP) diff --git a/src/calibre/ebooks/metadata/topaz.py b/src/calibre/ebooks/metadata/topaz.py index 137f750dda..ce0f0eb848 100644 --- a/src/calibre/ebooks/metadata/topaz.py +++ b/src/calibre/ebooks/metadata/topaz.py @@ -52,20 +52,20 @@ class StreamSlicer: start, stop = stop, start size = stop - start if size <= 0: - return b"" + return b'' stream.seek(base + start) data = stream.read(size) if stride != 1: data = data[::stride] return data - raise TypeError("stream indices must be integers") + raise TypeError('stream indices must be integers') def __setitem__(self, key, value): stream = self._stream base = self.start if isinstance(key, numbers.Integral): if len(value) != 1: - raise ValueError("key and value lengths must match") + raise ValueError('key and value lengths must match') stream.seek(base + key) return stream.write(value) if isinstance(key, slice): @@ -76,10 +76,10 @@ class StreamSlicer: if stride != 1: value = value[::stride] if len(value) != size: - raise ValueError("key and value lengths must match") + raise ValueError('key and value lengths must match') stream.seek(base + start) return stream.write(value) - raise TypeError("stream indices must be integers") + raise TypeError('stream indices must be integers') def update(self, data_blocks): # Rewrite the stream @@ -144,13 +144,13 @@ class MetadataUpdater: def dump_headers(self): ''' Diagnostic ''' - print("\ndump_headers():") + print('\ndump_headers():') for tag in self.topaz_headers: - print("%s: " % (tag)) + print('%s: ' % (tag)) num_recs = len(self.topaz_headers[tag]['blocks']) - print(" num_recs: %d" % num_recs) + print(' num_recs: %d' % num_recs) if num_recs: - print(" starting offset: 0x%x" % self.topaz_headers[tag]['blocks'][0]['offset']) + print(' starting offset: 0x%x' % self.topaz_headers[tag]['blocks'][0]['offset']) def dump_hex(self, src, length=16): ''' Diagnostic ''' @@ -159,9 +159,9 @@ class MetadataUpdater: result='' while src: s,src = src[:length],src[length:] - hexa = ' '.join(["%02X"%ord(x) for x in s]) + hexa = ' '.join(['%02X'%ord(x) for x in s]) s = s.translate(FILTER) - result += "%04X %-*s %s\n" % (N, length*3, hexa, s) + result += '%04X %-*s %s\n' % (N, length*3, hexa, s) N+=length print(result) @@ -394,7 +394,7 @@ if __name__ == '__main__': stream = io.BytesIO() with open(sys.argv[1], 'rb') as data: stream.write(data.read()) - mi = MetaInformation(title="Updated Title", authors=['Author, Random']) + mi = MetaInformation(title='Updated Title', authors=['Author, Random']) set_metadata(stream, mi) # Write the result diff --git a/src/calibre/ebooks/mobi/debug/headers.py b/src/calibre/ebooks/mobi/debug/headers.py index 15677599f6..392687c62d 100644 --- a/src/calibre/ebooks/mobi/debug/headers.py +++ b/src/calibre/ebooks/mobi/debug/headers.py @@ -40,7 +40,7 @@ class PalmDOCAttributes: ('Backup this database', 0x08), ('Okay to install newer over existing copy, if present on PalmPilot', 0x10), ('Force the PalmPilot to reset after this database is installed', 0x12), - ('Don\'t allow copy of file to be beamed to other Pilot', + ("Don't allow copy of file to be beamed to other Pilot", 0x14)]: self.attributes.append(PalmDOCAttributes.Attr(name, field, self.val)) diff --git a/src/calibre/ebooks/mobi/langcodes.py b/src/calibre/ebooks/mobi/langcodes.py index 26b22ce847..2adc1b74bc 100644 --- a/src/calibre/ebooks/mobi/langcodes.py +++ b/src/calibre/ebooks/mobi/langcodes.py @@ -13,87 +13,87 @@ lang_codes = { } main_language = { - 0 : "NEUTRAL", - 54 : "AFRIKAANS", - 28 : "ALBANIAN", - 1 : "ARABIC", - 43 : "ARMENIAN", - 77 : "ASSAMESE", - 44 : "AZERI", - 45 : "BASQUE", - 35 : "BELARUSIAN", - 69 : "BENGALI", - 2 : "BULGARIAN", - 3 : "CATALAN", - 4 : "CHINESE", + 0 : 'NEUTRAL', + 54 : 'AFRIKAANS', + 28 : 'ALBANIAN', + 1 : 'ARABIC', + 43 : 'ARMENIAN', + 77 : 'ASSAMESE', + 44 : 'AZERI', + 45 : 'BASQUE', + 35 : 'BELARUSIAN', + 69 : 'BENGALI', + 2 : 'BULGARIAN', + 3 : 'CATALAN', + 4 : 'CHINESE', # 26 : "CROATIAN", - 5 : "CZECH", - 6 : "DANISH", - 19 : "DUTCH", - 9 : "ENGLISH", - 37 : "ESTONIAN", - 56 : "FAEROESE", - 41 : "FARSI", - 11 : "FINNISH", - 12 : "FRENCH", - 55 : "GEORGIAN", - 7 : "GERMAN", - 8 : "GREEK", - 71 : "GUJARATI", - 13 : "HEBREW", - 57 : "HINDI", - 14 : "HUNGARIAN", - 15 : "ICELANDIC", - 33 : "INDONESIAN", - 16 : "ITALIAN", - 17 : "JAPANESE", - 75 : "KANNADA", - 63 : "KAZAK", - 87 : "KONKANI", - 18 : "KOREAN", - 38 : "LATVIAN", - 39 : "LITHUANIAN", - 47 : "MACEDONIAN", - 62 : "MALAY", - 76 : "MALAYALAM", - 58 : "MALTESE", - 78 : "MARATHI", - 97 : "NEPALI", - 20 : "NORWEGIAN", - 72 : "ORIYA", - 21 : "POLISH", - 22 : "PORTUGUESE", - 70 : "PUNJABI", - 23 : "RHAETOROMANIC", - 24 : "ROMANIAN", - 25 : "RUSSIAN", - 59 : "SAMI", - 79 : "SANSKRIT", - 26 : "SERBIAN", - 27 : "SLOVAK", - 36 : "SLOVENIAN", - 46 : "SORBIAN", - 10 : "SPANISH", - 48 : "SUTU", - 65 : "SWAHILI", - 29 : "SWEDISH", - 73 : "TAMIL", - 68 : "TATAR", - 74 : "TELUGU", - 30 : "THAI", - 49 : "TSONGA", - 50 : "TSWANA", - 31 : "TURKISH", - 34 : "UKRAINIAN", - 32 : "URDU", - 67 : "UZBEK", - 42 : "VIETNAMESE", - 52 : "XHOSA", - 53 : "ZULU", + 5 : 'CZECH', + 6 : 'DANISH', + 19 : 'DUTCH', + 9 : 'ENGLISH', + 37 : 'ESTONIAN', + 56 : 'FAEROESE', + 41 : 'FARSI', + 11 : 'FINNISH', + 12 : 'FRENCH', + 55 : 'GEORGIAN', + 7 : 'GERMAN', + 8 : 'GREEK', + 71 : 'GUJARATI', + 13 : 'HEBREW', + 57 : 'HINDI', + 14 : 'HUNGARIAN', + 15 : 'ICELANDIC', + 33 : 'INDONESIAN', + 16 : 'ITALIAN', + 17 : 'JAPANESE', + 75 : 'KANNADA', + 63 : 'KAZAK', + 87 : 'KONKANI', + 18 : 'KOREAN', + 38 : 'LATVIAN', + 39 : 'LITHUANIAN', + 47 : 'MACEDONIAN', + 62 : 'MALAY', + 76 : 'MALAYALAM', + 58 : 'MALTESE', + 78 : 'MARATHI', + 97 : 'NEPALI', + 20 : 'NORWEGIAN', + 72 : 'ORIYA', + 21 : 'POLISH', + 22 : 'PORTUGUESE', + 70 : 'PUNJABI', + 23 : 'RHAETOROMANIC', + 24 : 'ROMANIAN', + 25 : 'RUSSIAN', + 59 : 'SAMI', + 79 : 'SANSKRIT', + 26 : 'SERBIAN', + 27 : 'SLOVAK', + 36 : 'SLOVENIAN', + 46 : 'SORBIAN', + 10 : 'SPANISH', + 48 : 'SUTU', + 65 : 'SWAHILI', + 29 : 'SWEDISH', + 73 : 'TAMIL', + 68 : 'TATAR', + 74 : 'TELUGU', + 30 : 'THAI', + 49 : 'TSONGA', + 50 : 'TSWANA', + 31 : 'TURKISH', + 34 : 'UKRAINIAN', + 32 : 'URDU', + 67 : 'UZBEK', + 42 : 'VIETNAMESE', + 52 : 'XHOSA', + 53 : 'ZULU', } sub_language = { - 0 : "NEUTRAL", + 0 : 'NEUTRAL', # 1 : "ARABIC_SAUDI_ARABIA", # 2 : "ARABIC_IRAQ", # 3 : "ARABIC_EGYPT", @@ -140,30 +140,30 @@ sub_language = { # 2 : "PORTUGUESE", # 1 : "PORTUGUESE_BRAZILIAN", # 2 : "SERBIAN_LATIN", - 3 : "SERBIAN_CYRILLIC", + 3 : 'SERBIAN_CYRILLIC', # 1 : "SPANISH", # 2 : "SPANISH_MEXICAN", - 4 : "SPANISH_GUATEMALA", - 5 : "SPANISH_COSTA_RICA", - 6 : "SPANISH_PANAMA", - 7 : "SPANISH_DOMINICAN_REPUBLIC", - 8 : "SPANISH_VENEZUELA", - 9 : "SPANISH_COLOMBIA", - 10 : "SPANISH_PERU", - 11 : "SPANISH_ARGENTINA", - 12 : "SPANISH_ECUADOR", - 13 : "SPANISH_CHILE", - 14 : "SPANISH_URUGUAY", - 15 : "SPANISH_PARAGUAY", - 16 : "SPANISH_BOLIVIA", - 17 : "SPANISH_EL_SALVADOR", - 18 : "SPANISH_HONDURAS", - 19 : "SPANISH_NICARAGUA", - 20 : "SPANISH_PUERTO_RICO", + 4 : 'SPANISH_GUATEMALA', + 5 : 'SPANISH_COSTA_RICA', + 6 : 'SPANISH_PANAMA', + 7 : 'SPANISH_DOMINICAN_REPUBLIC', + 8 : 'SPANISH_VENEZUELA', + 9 : 'SPANISH_COLOMBIA', + 10 : 'SPANISH_PERU', + 11 : 'SPANISH_ARGENTINA', + 12 : 'SPANISH_ECUADOR', + 13 : 'SPANISH_CHILE', + 14 : 'SPANISH_URUGUAY', + 15 : 'SPANISH_PARAGUAY', + 16 : 'SPANISH_BOLIVIA', + 17 : 'SPANISH_EL_SALVADOR', + 18 : 'SPANISH_HONDURAS', + 19 : 'SPANISH_NICARAGUA', + 20 : 'SPANISH_PUERTO_RICO', # 1 : "SWEDISH", # 2 : "SWEDISH_FINLAND", - 1 : "UZBEK_LATIN", - 2 : "UZBEK_CYRILLIC", + 1 : 'UZBEK_LATIN', + 2 : 'UZBEK_CYRILLIC', } IANA_MOBI = \ diff --git a/src/calibre/ebooks/mobi/mobiml.py b/src/calibre/ebooks/mobi/mobiml.py index 3d6aa19148..d2d5058f81 100644 --- a/src/calibre/ebooks/mobi/mobiml.py +++ b/src/calibre/ebooks/mobi/mobiml.py @@ -160,8 +160,8 @@ class MobiMLizer: return ptsize embase = self.profile.fbase if round(ptsize) < embase: - return "%dpt" % int(round(ptsize)) - return "%dem" % int(round(ptsize / embase)) + return '%dpt' % int(round(ptsize)) + return '%dem' % int(round(ptsize / embase)) def preize_text(self, text, pre_wrap=False): text = str(text) @@ -503,7 +503,7 @@ class MobiMLizer: istate.attrib['width'] = raww else: prop = style['width'] / self.profile.width - istate.attrib['width'] = "%d%%" % int(round(prop * 100)) + istate.attrib['width'] = '%d%%' % int(round(prop * 100)) elif display == 'table': tag = 'table' elif display == 'table-row': diff --git a/src/calibre/ebooks/mobi/reader/index.py b/src/calibre/ebooks/mobi/reader/index.py index aeb2f5d1d7..fe7b69c672 100644 --- a/src/calibre/ebooks/mobi/reader/index.py +++ b/src/calibre/ebooks/mobi/reader/index.py @@ -193,7 +193,7 @@ def get_tag_map(control_byte_count, tagx, data, strict=False): total_consumed += consumed values.append(byts) if total_consumed != x.value_bytes: - err = ("Error: Should consume %s bytes, but consumed %s" % + err = ('Error: Should consume %s bytes, but consumed %s' % (x.value_bytes, total_consumed)) if strict: raise ValueError(err) @@ -202,7 +202,7 @@ def get_tag_map(control_byte_count, tagx, data, strict=False): ans[x.tag] = values # Test that all bytes have been processed if data.replace(b'\0', b''): - err = ("Warning: There are unprocessed index bytes left: %s" % + err = ('Warning: There are unprocessed index bytes left: %s' % format_bytes(data)) if strict: raise ValueError(err) diff --git a/src/calibre/ebooks/mobi/reader/markup.py b/src/calibre/ebooks/mobi/reader/markup.py index 3578d63dcb..a65885f721 100644 --- a/src/calibre/ebooks/mobi/reader/markup.py +++ b/src/calibre/ebooks/mobi/reader/markup.py @@ -83,7 +83,7 @@ def remove_kindlegen_markup(parts, aid_anchor_suffix, linked_aids): replacement = ' id="%s"' % (aid + '-' + aid_anchor_suffix) tag = within_tag_aid_position_pattern.sub(replacement, tag, 1) srcpieces[j] = tag - part = "".join(srcpieces) + part = ''.join(srcpieces) parts[i] = part # we can safely remove all of the Kindlegen generated data-AmznPageBreak @@ -101,7 +101,7 @@ def remove_kindlegen_markup(parts, aid_anchor_suffix, linked_aids): if tag.startswith('<'): srcpieces[j] = within_tag_AmznPageBreak_position_pattern.sub( lambda m:' style="page-break-after:%s"'%m.group(1), tag) - part = "".join(srcpieces) + part = ''.join(srcpieces) parts[i] = part @@ -156,7 +156,7 @@ def update_flow_links(mobi8_reader, resource_map, log): log.warn('Referenced image %s was not recognized ' 'as a valid image in %s' % (num, tag)) srcpieces[j] = tag - flow = "".join(srcpieces) + flow = ''.join(srcpieces) # replacements inside css url(): srcpieces = url_pattern.split(flow) @@ -195,7 +195,7 @@ def update_flow_links(mobi8_reader, resource_map, log): tag = url_css_index_pattern.sub(replacement, tag, 1) srcpieces[j] = tag - flow = "".join(srcpieces) + flow = ''.join(srcpieces) # flow pattern not inside url() srcpieces = re.split(tag_pattern, flow) @@ -217,7 +217,7 @@ def update_flow_links(mobi8_reader, resource_map, log): replacement = '"../' + fi.dir + '/' + fi.fname + '"' tag = flow_pattern.sub(replacement, tag, 1) srcpieces[j] = tag - flow = "".join(srcpieces) + flow = ''.join(srcpieces) flows.append(flow) @@ -253,7 +253,7 @@ def insert_flows_into_markup(parts, flows, mobi8_reader, log): replacement = '"../' + fi.dir + '/' + fi.fname + '"' tag = flow_pattern.sub(replacement, tag, 1) srcpieces[j] = tag - part = "".join(srcpieces) + part = ''.join(srcpieces) # store away modified version parts[i] = part @@ -286,7 +286,7 @@ def insert_images_into_markup(parts, resource_map, log): log.warn('Referenced image %s was not recognized as ' 'a valid image in %s' % (num, tag)) srcpieces[j] = tag - part = "".join(srcpieces) + part = ''.join(srcpieces) # store away modified version parts[i] = part @@ -309,7 +309,7 @@ def insert_images_into_markup(parts, resource_map, log): log.warn('Referenced image %s was not recognized as ' 'a valid image in %s' % (num, tag)) srcpieces[j] = tag - part = "".join(srcpieces) + part = ''.join(srcpieces) # store away modified version parts[i] = part @@ -328,7 +328,7 @@ def upshift_markup(parts): tag = tag.replace('preserveaspectratio','preserveAspectRatio') tag = tag.replace('viewbox','viewBox') srcpieces[j] = tag - part = "".join(srcpieces) + part = ''.join(srcpieces) # store away modified version parts[i] = part diff --git a/src/calibre/ebooks/mobi/reader/mobi6.py b/src/calibre/ebooks/mobi/reader/mobi6.py index b6051136d8..68aaf49aca 100644 --- a/src/calibre/ebooks/mobi/reader/mobi6.py +++ b/src/calibre/ebooks/mobi/reader/mobi6.py @@ -535,7 +535,7 @@ class MobiReader: try: nval = float(val[:-2]) nval *= 16 * (168.451/72) # Assume this was set using the Kindle profile - attrib[attr] = "%dpx"%int(nval) + attrib[attr] = '%dpx'%int(nval) except: del attrib[attr] elif val.lower().endswith('%'): @@ -560,7 +560,7 @@ class MobiReader: if 'filepos' in attrib: filepos = attrib.pop('filepos') try: - attrib['href'] = "#filepos%d" % int(filepos) + attrib['href'] = '#filepos%d' % int(filepos) except ValueError: pass if (tag.tag == 'a' and attrib.get('id', '').startswith('filepos') and diff --git a/src/calibre/ebooks/mobi/reader/mobi8.py b/src/calibre/ebooks/mobi/reader/mobi8.py index 558dfb7da0..8223fcbe74 100644 --- a/src/calibre/ebooks/mobi/reader/mobi8.py +++ b/src/calibre/ebooks/mobi/reader/mobi8.py @@ -272,7 +272,7 @@ class Mobi8Reader: flowpart = from_svg else: format = 'file' - dir = "images" + dir = 'images' fname = 'svgimg' + nstr + '.svg' else: # search for CDATA and if exists inline it @@ -286,7 +286,7 @@ class Mobi8Reader: # css - assume as standalone css file typ = 'css' format = 'file' - dir = "styles" + dir = 'styles' fname = nstr + '.css' self.flows[j] = flowpart @@ -430,7 +430,7 @@ class Mobi8Reader: pass # Ignore these records elif typ == b'FONT': font = read_font_record(data) - href = "fonts/%05d.%s" % (fname_idx, font['ext']) + href = 'fonts/%05d.%s' % (fname_idx, font['ext']) if font['err']: self.log.warn('Reading font record %d failed: %s'%( fname_idx, font['err'])) diff --git a/src/calibre/ebooks/mobi/reader/ncx.py b/src/calibre/ebooks/mobi/reader/ncx.py index b9f9a735cb..756b955ad5 100644 --- a/src/calibre/ebooks/mobi/reader/ncx.py +++ b/src/calibre/ebooks/mobi/reader/ncx.py @@ -35,9 +35,9 @@ default_entry = { 'pos': -1, 'len': 0, 'noffs': -1, - 'text' : "Unknown Text", + 'text' : 'Unknown Text', 'hlvl' : -1, - 'kind' : "Unknown Class", + 'kind' : 'Unknown Class', 'pos_fid' : None, 'parent' : -1, 'child1' : -1, diff --git a/src/calibre/ebooks/mobi/writer2/serializer.py b/src/calibre/ebooks/mobi/writer2/serializer.py index fd25a58f39..928f5712ec 100644 --- a/src/calibre/ebooks/mobi/writer2/serializer.py +++ b/src/calibre/ebooks/mobi/writer2/serializer.py @@ -223,7 +223,7 @@ class Serializer: buf.write(b'<mbp:pagebreak />') self.id_offsets[urlnormalize(href)] = buf.tell() - if tocref.klass == "periodical": + if tocref.klass == 'periodical': buf.write(b'<div> <div height="1em"></div>') else: t = tocref.title diff --git a/src/calibre/ebooks/mobi/writer8/exth.py b/src/calibre/ebooks/mobi/writer8/exth.py index ff69a45eef..24c8024136 100644 --- a/src/calibre/ebooks/mobi/writer8/exth.py +++ b/src/calibre/ebooks/mobi/writer8/exth.py @@ -142,7 +142,7 @@ def build_exth(metadata, prefer_author_sort=False, is_periodical=False, datestr = str(metadata['timestamp'][0]) if datestr is None: - raise ValueError("missing date or timestamp") + raise ValueError('missing date or timestamp') datestr = datestr.encode('utf-8') exth.write(pack(b'>II', EXTH_CODES['pubdate'], len(datestr) + 8)) diff --git a/src/calibre/ebooks/mobi/writer8/main.py b/src/calibre/ebooks/mobi/writer8/main.py index 08f4c560e7..1cc0da1f7c 100644 --- a/src/calibre/ebooks/mobi/writer8/main.py +++ b/src/calibre/ebooks/mobi/writer8/main.py @@ -241,7 +241,7 @@ class KF8Writer: p = svg.getparent() pos = p.index(svg) img = etree.Element(XHTML('img'), - src="kindle:flow:%s?mime=image/svg+xml"%to_ref(idx)) + src='kindle:flow:%s?mime=image/svg+xml'%to_ref(idx)) p.insert(pos, img) extract(svg) diff --git a/src/calibre/ebooks/odt/input.py b/src/calibre/ebooks/odt/input.py index 73e0c21e8d..9ea8aa8ed1 100644 --- a/src/calibre/ebooks/odt/input.py +++ b/src/calibre/ebooks/odt/input.py @@ -239,14 +239,14 @@ class Extract(ODF2XHTML): # now it should be safe to remove the text:p parent = para.parentNode parent.removeChild(para) - log("Removed cover image paragraph from document...") + log('Removed cover image paragraph from document...') break def filter_load(self, odffile, mi, log): - """ This is an adaption from ODF2XHTML. It adds a step between + ''' This is an adaption from ODF2XHTML. It adds a step between load and parse of the document where the Element tree can be modified. - """ + ''' # first load the odf structure self.lines = [] self._wfunc = self._wlines diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index 09d242f222..1a7a5e3f63 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -463,13 +463,13 @@ del USAFE def urlquote(href): - """ Quote URL-unsafe characters, allowing IRI-safe characters. + ''' Quote URL-unsafe characters, allowing IRI-safe characters. That is, this function returns valid IRIs not valid URIs. In particular, - IRIs can contain non-ascii characters. """ + IRIs can contain non-ascii characters. ''' result = [] isbytes = isinstance(href, bytes) unsafe = URL_UNSAFE[int(isbytes)] - esc, join = "%%%02x", '' + esc, join = '%%%02x', '' if isbytes: esc, join = esc.encode('ascii'), b'' for char in href: @@ -480,9 +480,9 @@ def urlquote(href): def urlnormalize(href): - """Convert a URL into normalized form, with all and only URL-unsafe + '''Convert a URL into normalized form, with all and only URL-unsafe characters URL quoted. - """ + ''' try: parts = urlparse(href) except ValueError as e: @@ -497,11 +497,11 @@ def urlnormalize(href): def extract(elem): - """ + ''' Removes this element from the tree, including its children and text. The tail text is joined to the previous element or parent. - """ + ''' parent = elem.getparent() if parent is not None: if elem.tail: @@ -535,15 +535,15 @@ _css_logger.addHandler(_css_log_handler) class OEBError(Exception): - """Generic OEB-processing error.""" + '''Generic OEB-processing error.''' pass class NullContainer: - """An empty container. + '''An empty container. For use with book formats which do not support container-like access. - """ + ''' def __init__(self, log): self.log = log @@ -562,7 +562,7 @@ class NullContainer: class DirContainer: - """Filesystem directory container.""" + '''Filesystem directory container.''' def __init__(self, path, log, ignore_opf=False): self.log = log @@ -664,7 +664,7 @@ class Metadata: 'xsi': XSI_NS, 'calibre': CALIBRE_NS} class Item: - """An item of OEB data model metadata. + '''An item of OEB data model metadata. The metadata term or name may be accessed via the :attr:`term` or :attr:`name` attributes. The metadata value or content may be accessed @@ -675,9 +675,9 @@ class Metadata: fully-qualified names using the Python container access syntax, or via their local names using Python attribute syntax. Only attributes allowed by the OPF 2.0 specification are supported. - """ + ''' class Attribute: - """Smart accessor for allowed OEB metadata item attributes.""" + '''Smart accessor for allowed OEB metadata item attributes.''' def __init__(self, attr, allowed=None): if not callable(attr): @@ -815,7 +815,7 @@ class Metadata: self.primary_writing_mode = None def add(self, term, value, attrib={}, nsmap={}, **kwargs): - """Add a new metadata item.""" + '''Add a new metadata item.''' item = self.Item(term, value, attrib, nsmap, **kwargs) items = self.items[barename(item.term)] items.append(item) @@ -894,7 +894,7 @@ class Metadata: class Manifest: - """Collection of files composing an OEB data model book. + '''Collection of files composing an OEB data model book. Provides access to the content of the files composing the book and attributes associated with those files, including their internal paths, @@ -907,7 +907,7 @@ class Manifest: the manifest items and the values are the items themselves. :attr:`hrefs`: A dictionary in which the keys are the internal paths of the manifest items and the values are the items themselves. - """ + ''' class Item: """An OEB data model book content file. @@ -1138,15 +1138,15 @@ class Manifest: return sp, (self.media_type or '').lower(), numeric_sort_key(href), self.id def relhref(self, href): - """Convert the URL provided in :param:`href` from a book-absolute + '''Convert the URL provided in :param:`href` from a book-absolute reference to a reference relative to this manifest item. - """ + ''' return rel_href(self.href, href) def abshref(self, href): - """Convert the URL provided in :param:`href` from a reference + '''Convert the URL provided in :param:`href` from a reference relative to this manifest item to a book-absolute reference. - """ + ''' try: purl = urlparse(href) except ValueError: @@ -1194,7 +1194,7 @@ class Manifest: return item def remove(self, item): - """Removes :param:`item` from the manifest.""" + '''Removes :param:`item` from the manifest.''' if item in self.ids: item = self.ids[item] del self.ids[item.id] @@ -1211,14 +1211,14 @@ class Manifest: self.items.remove(item) def generate(self, id=None, href=None): - """Generate a new unique identifier and/or internal path for use in + '''Generate a new unique identifier and/or internal path for use in creating a new manifest item, using the provided :param:`id` and/or :param:`href` as bases. Returns an two-tuple of the new id and path. If either :param:`id` or :param:`href` are `None` then the corresponding item in the return tuple will also be `None`. - """ + ''' if id is not None: base = id index = 1 @@ -1316,14 +1316,14 @@ class Spine: return linear def add(self, item, linear=None): - """Append :param:`item` to the end of the `Spine`.""" + '''Append :param:`item` to the end of the `Spine`.''' item.linear = self._linear(linear) item.spine_position = len(self.items) self.items.append(item) return item def insert(self, index, item, linear): - """Insert :param:`item` at position :param:`index` in the `Spine`.""" + '''Insert :param:`item` at position :param:`index` in the `Spine`.''' item.linear = self._linear(linear) item.spine_position = index self.items.insert(index, item) @@ -1332,7 +1332,7 @@ class Spine: return item def remove(self, item): - """Remove :param:`item` from the `Spine`.""" + '''Remove :param:`item` from the `Spine`.''' index = item.spine_position self.items.pop(index) for i in range(index, len(self.items)): @@ -1375,15 +1375,15 @@ class Spine: class Guide: - """Collection of references to standard frequently-occurring sections + '''Collection of references to standard frequently-occurring sections within an OEB data model book. Provides dictionary-like access, in which the keys are the OEB reference type identifiers and the values are `Reference` objects. - """ + ''' class Reference: - """Reference to a standard book section. + '''Reference to a standard book section. Provides the following instance data members: @@ -1392,7 +1392,7 @@ class Guide: :attr:`title`: Human-readable section title. :attr:`href`: Book-internal URL of the referenced section. May include a fragment identifier. - """ + ''' _TYPES_TITLES = [('cover', __('Cover')), ('title-page', __('Title page')), ('toc', __('Table of Contents')), @@ -1433,7 +1433,7 @@ class Guide: @property def item(self): - """The manifest item associated with this reference.""" + '''The manifest item associated with this reference.''' path = urldefrag(self.href)[0] hrefs = self.oeb.manifest.hrefs return hrefs.get(path, None) @@ -1443,7 +1443,7 @@ class Guide: self.refs = {} def add(self, type, title, href): - """Add a new reference to the `Guide`.""" + '''Add a new reference to the `Guide`.''' if href: href = str(href) ref = self.Reference(self.oeb, type, title, href) @@ -1505,7 +1505,7 @@ class Guide: class TOC: - """Represents a hierarchical table of contents or navigation tree for + '''Represents a hierarchical table of contents or navigation tree for accessing arbitrary semantic sections within an OEB data model book. Acts as a node within the navigation tree. Provides list-like access to @@ -1518,7 +1518,7 @@ class TOC: :attr:`author`: Optional author attribution for periodicals <mbp:> :attr:`description`: Optional description attribute for periodicals <mbp:> :attr:`toc_thumbnail`: Optional toc thumbnail image - """ + ''' def __init__(self, title=None, href=None, klass=None, id=None, play_order=None, author=None, description=None, toc_thumbnail=None): @@ -1536,7 +1536,7 @@ class TOC: self.toc_thumbnail = toc_thumbnail def add(self, title, href, klass=None, id=None, play_order=0, author=None, description=None, toc_thumbnail=None): - """Create and return a new sub-node of this node.""" + '''Create and return a new sub-node of this node.''' node = TOC(title, href, klass, id, play_order, author, description, toc_thumbnail) self.nodes.append(node) return node @@ -1552,7 +1552,7 @@ class TOC: return False def iter(self): - """Iterate over this node and all descendants in depth-first order.""" + '''Iterate over this node and all descendants in depth-first order.''' yield self for child in self.nodes: yield from child.iter() @@ -1578,7 +1578,7 @@ class TOC: return False def iterdescendants(self, breadth_first=False): - """Iterate over all descendant nodes in depth-first order.""" + '''Iterate over all descendant nodes in depth-first order.''' if breadth_first: for child in self.nodes: yield child @@ -1589,16 +1589,16 @@ class TOC: yield from child.iter() def __iter__(self): - """Iterate over all immediate child nodes.""" + '''Iterate over all immediate child nodes.''' yield from self.nodes def __getitem__(self, index): return self.nodes[index] def autolayer(self): - """Make sequences of children pointing to the same content file into + '''Make sequences of children pointing to the same content file into children of the first node referencing that file. - """ + ''' prev = None for node in list(self.nodes): if prev and urldefrag(prev.href)[0] == urldefrag(node.href)[0]: @@ -1608,7 +1608,7 @@ class TOC: prev = node def depth(self): - """The maximum depth of the navigation tree rooted at this node.""" + '''The maximum depth of the navigation tree rooted at this node.''' try: return max(node.depth() for node in self.nodes) + 1 except ValueError: @@ -1719,7 +1719,7 @@ class PageList: self.pages = [] def add(self, name, href, type='normal', klass=None, id=None): - """Create a new page and add it to the `PageList`.""" + '''Create a new page and add it to the `PageList`.''' page = self.Page(name, href, type, klass, id) self.pages.append(page) return page @@ -1763,7 +1763,7 @@ class PageList: class OEBBook: - """Representation of a book in the IDPF OEB data model.""" + '''Representation of a book in the IDPF OEB data model.''' COVER_SVG_XP = XPath('h:body//svg:svg[position() = 1]') COVER_OBJECT_XP = XPath('h:body//h:object[@data][position() = 1]') @@ -1773,7 +1773,7 @@ class OEBBook: css_preprocessor=CSSPreProcessor(), encoding='utf-8', pretty_print=False, input_encoding='utf-8'): - """Create empty book. Arguments: + '''Create empty book. Arguments: :param:`encoding`: Default encoding for textual content read from an external container. @@ -1802,7 +1802,7 @@ class OEBBook: :attr:`toc`: Hierarchical table of contents. :attr:`pages`: List of "pages," such as indexed to a print edition of the same text. - """ + ''' _css_log_handler.log = logger self.encoding = encoding self.input_encoding = input_encoding @@ -1841,7 +1841,7 @@ class OEBBook: @classmethod def generate(cls, opts): - """Generate an OEBBook instance from command-line options.""" + '''Generate an OEBBook instance from command-line options.''' encoding = opts.encoding pretty_print = opts.pretty_print return cls(encoding=encoding, pretty_print=pretty_print) @@ -1853,7 +1853,7 @@ class OEBBook: return translate(lang, text) def decode(self, data): - """Automatically decode :param:`data` into a `unicode` object.""" + '''Automatically decode :param:`data` into a `unicode` object.''' def fix_data(d): return d.replace('\r\n', '\n').replace('\r', '\n') if isinstance(data, str): @@ -1998,8 +1998,8 @@ class OEBBook: def rel_href(base_href, href): - """Convert the URL provided in :param:`href` to a URL relative to the URL - in :param:`base_href` """ + '''Convert the URL provided in :param:`href` to a URL relative to the URL + in :param:`base_href` ''' if urlparse(href).scheme: return href if '/' not in base_href: diff --git a/src/calibre/ebooks/oeb/polish/check/links.py b/src/calibre/ebooks/oeb/polish/check/links.py index de558d997f..d45b345301 100644 --- a/src/calibre/ebooks/oeb/polish/check/links.py +++ b/src/calibre/ebooks/oeb/polish/check/links.py @@ -189,7 +189,7 @@ class Bookmarks(BadLink): HELP = _( 'This file stores the bookmarks and last opened information from' ' the calibre E-book viewer. You can remove it if you do not' - ' need that information, or don\'t want to share it with' + " need that information, or don't want to share it with" ' other people you send this book to.') INDIVIDUAL_FIX = _('Remove this file') level = INFO @@ -454,7 +454,7 @@ def check_external_links(container, progress_callback=(lambda num, total:None), done.append(None) progress_callback(len(done), len(external_links)) - workers = [Thread(name="CheckLinks", target=check_links) for i in range(min(10, len(external_links)))] + workers = [Thread(name='CheckLinks', target=check_links) for i in range(min(10, len(external_links)))] for w in workers: w.daemon = True w.start() diff --git a/src/calibre/ebooks/oeb/polish/container.py b/src/calibre/ebooks/oeb/polish/container.py index 7aaea2408f..0bc2f598fa 100644 --- a/src/calibre/ebooks/oeb/polish/container.py +++ b/src/calibre/ebooks/oeb/polish/container.py @@ -163,11 +163,11 @@ class ContainerBase: # {{{ return adjust_mime_for_epub(filename=name, opf_version=self.opf_version_parsed) def decode(self, data, normalize_to_nfc=True): - """ + ''' Automatically decode ``data`` into a ``unicode`` object. :param normalize_to_nfc: Normalize returned unicode to the NFC normal form as is required by both the EPUB and AZW3 formats. - """ + ''' def fix_data(d): return d.replace('\r\n', '\n').replace('\r', '\n') if isinstance(data, str): @@ -911,12 +911,12 @@ class Container(ContainerBase): # {{{ metadata = self.opf_xpath('//opf:metadata')[0] total_duration = 0 for item_id, duration in (duration_map or {}).items(): - meta = metadata.makeelement(OPF('meta'), property="media:duration", refines="#" + item_id) + meta = metadata.makeelement(OPF('meta'), property='media:duration', refines='#' + item_id) meta.text = seconds_to_timestamp(duration) self.insert_into_xml(metadata, meta) total_duration += duration if duration_map: - meta = metadata.makeelement(OPF('meta'), property="media:duration") + meta = metadata.makeelement(OPF('meta'), property='media:duration') meta.text = seconds_to_timestamp(total_duration) self.insert_into_xml(metadata, meta) diff --git a/src/calibre/ebooks/oeb/polish/cover.py b/src/calibre/ebooks/oeb/polish/cover.py index 3e34c15aa7..e250fccd8e 100644 --- a/src/calibre/ebooks/oeb/polish/cover.py +++ b/src/calibre/ebooks/oeb/polish/cover.py @@ -379,7 +379,7 @@ def create_epub_cover(container, cover_path, existing_image, options=None): with open(cover_path, 'rb') as csrc: width, height = identify(csrc)[1:] except: - container.log.exception("Failed to get width and height of cover") + container.log.exception('Failed to get width and height of cover') ar = 'xMidYMid meet' if keep_aspect else 'none' templ = CoverManager.SVG_TEMPLATE.replace('__ar__', ar) templ = templ.replace('__viewbox__', '0 0 %d %d'%(width, height)) diff --git a/src/calibre/ebooks/oeb/polish/create.py b/src/calibre/ebooks/oeb/polish/create.py index c1c9900917..74bf3faf0e 100644 --- a/src/calibre/ebooks/oeb/polish/create.py +++ b/src/calibre/ebooks/oeb/polish/create.py @@ -80,7 +80,7 @@ def create_book(mi, path, fmt='epub', opf_name='metadata.opf', html_name='start. i = m.makeelement('{%s}item' % opfns, href=toc_name, id='ncx') i.set('media-type', guess_type(toc_name)) m.append(i) - s = opf.makeelement('{%s}spine' % opfns, toc="ncx") + s = opf.makeelement('{%s}spine' % opfns, toc='ncx') opf.insert(2, s) i = s.makeelement('{%s}itemref' % opfns, idref='start') s.append(i) diff --git a/src/calibre/ebooks/oeb/polish/tests/parsing.py b/src/calibre/ebooks/oeb/polish/tests/parsing.py index b65b697af0..cf89a9e0c8 100644 --- a/src/calibre/ebooks/oeb/polish/tests/parsing.py +++ b/src/calibre/ebooks/oeb/polish/tests/parsing.py @@ -132,7 +132,7 @@ def entities(test, parse_function): markup = '<html><p> '</p>' root = parse_function(markup) err = 'Entities not handled, parsed markup:\n' + etree.tostring(root, encoding='unicode') - test.assertEqual('\xa0\'', root.xpath('//*[local-name()="p"]')[0].text, err) + test.assertEqual("\xa0'", root.xpath('//*[local-name()="p"]')[0].text, err) def multiple_html_and_body(test, parse_function): diff --git a/src/calibre/ebooks/oeb/polish/toc.py b/src/calibre/ebooks/oeb/polish/toc.py index 602b278940..24b5b79299 100644 --- a/src/calibre/ebooks/oeb/polish/toc.py +++ b/src/calibre/ebooks/oeb/polish/toc.py @@ -93,7 +93,7 @@ class TOC: @property def depth(self): - """The maximum depth of the navigation tree rooted at this node.""" + '''The maximum depth of the navigation tree rooted at this node.''' try: return max(node.depth for node in self) + 1 except ValueError: @@ -871,7 +871,7 @@ def toc_to_html(toc, container, toc_name, title, lang=None): E.body( E.h2(title), E.ul(), - id="calibre_generated_inline_toc", + id='calibre_generated_inline_toc', ) ) diff --git a/src/calibre/ebooks/oeb/reader.py b/src/calibre/ebooks/oeb/reader.py index 632cce3344..83141ee051 100644 --- a/src/calibre/ebooks/oeb/reader.py +++ b/src/calibre/ebooks/oeb/reader.py @@ -1,6 +1,6 @@ -""" +''' Container-/OPF-based input OEBBook reader. -""" +''' __license__ = 'GPL v3' @@ -59,36 +59,36 @@ __all__ = ['OEBReader'] class OEBReader: - """Read an OEBPS 1.x or OPF/OPS 2.0 file collection.""" + '''Read an OEBPS 1.x or OPF/OPS 2.0 file collection.''' COVER_SVG_XP = XPath('h:body//svg:svg[position() = 1]') COVER_OBJECT_XP = XPath('h:body//h:object[@data][position() = 1]') Container = DirContainer - """Container type used to access book files. Override in sub-classes.""" + '''Container type used to access book files. Override in sub-classes.''' DEFAULT_PROFILE = 'PRS505' - """Default renderer profile for content read with this Reader.""" + '''Default renderer profile for content read with this Reader.''' TRANSFORMS = [] - """List of transforms to apply to content read with this Reader.""" + '''List of transforms to apply to content read with this Reader.''' @classmethod def config(cls, cfg): - """Add any book-reading options to the :class:`Config` object + '''Add any book-reading options to the :class:`Config` object :param:`cfg`. - """ + ''' return @classmethod def generate(cls, opts): - """Generate a Reader instance from command-line options.""" + '''Generate a Reader instance from command-line options.''' return cls() def __call__(self, oeb, path): - """Read the book at :param:`path` into the :class:`OEBBook` object + '''Read the book at :param:`path` into the :class:`OEBBook` object :param:`oeb`. - """ + ''' self.oeb = oeb self.logger = self.log = oeb.logger oeb.container = self.Container(path, self.logger) @@ -368,7 +368,7 @@ class OEBReader: self.oeb.log.warn('The item %s is not a XML document.' ' Removing it from spine.'%item.href) if len(spine) == 0: - raise OEBError("Spine is empty") + raise OEBError('Spine is empty') self._spine_add_extra() for val in xpath(opf, '/o2:package/o2:spine/@page-progression-direction'): if val in {'ltr', 'rtl'}: diff --git a/src/calibre/ebooks/oeb/stylizer.py b/src/calibre/ebooks/oeb/stylizer.py index ce8ea1a828..8edd64fece 100644 --- a/src/calibre/ebooks/oeb/stylizer.py +++ b/src/calibre/ebooks/oeb/stylizer.py @@ -184,7 +184,7 @@ class StylizerRules: if size == 'smallest': size = 'xx-small' if size in FONT_SIZE_NAMES: - style['font-size'] = "%.1frem" % (self.profile.fnames[size] / float(self.profile.fbase)) + style['font-size'] = '%.1frem' % (self.profile.fnames[size] / float(self.profile.fbase)) if '-epub-writing-mode' in style: for x in ('-webkit-writing-mode', 'writing-mode'): style[x] = style.get(x, style['-epub-writing-mode']) @@ -421,7 +421,7 @@ class Stylizer: style['font-size'].endswith('pt'): style = copy.copy(style) size = float(style['font-size'][:-2]) - style['font-size'] = "%.2fpt" % (size * font_scale) + style['font-size'] = '%.2fpt' % (size * font_scale) style = ';\n '.join(': '.join(item) for item in style.items()) rules.append(f'{selector} {{\n {style};\n}}') return '\n'.join(rules) @@ -628,7 +628,7 @@ class Style: if not isinstance(result, numbers.Number): return base if result < 0: - result = normalize_fontsize("smaller", base) + result = normalize_fontsize('smaller', base) if factor: result = factor * base return result @@ -854,7 +854,7 @@ class Style: def __str__(self): items = sorted(iteritems(self._style)) - return '; '.join(f"{key}: {val}" for key, val in items) + return '; '.join(f'{key}: {val}' for key, val in items) def cssdict(self): return dict(self._style) diff --git a/src/calibre/ebooks/oeb/transforms/filenames.py b/src/calibre/ebooks/oeb/transforms/filenames.py index 36b55a30ce..485862fe39 100644 --- a/src/calibre/ebooks/oeb/transforms/filenames.py +++ b/src/calibre/ebooks/oeb/transforms/filenames.py @@ -154,7 +154,7 @@ class FlatFilenames: # {{{ for item in list(oeb.manifest.items): # Flatten URL by removing directories. # Example: a/b/c/index.html -> a_b_c_index.html - nhref = item.href.replace("/", "_") + nhref = item.href.replace('/', '_') if item.href == nhref: # URL hasn't changed, skip item. diff --git a/src/calibre/ebooks/oeb/transforms/flatcss.py b/src/calibre/ebooks/oeb/transforms/flatcss.py index 1b5e057d0d..cc8aeb7d8c 100644 --- a/src/calibre/ebooks/oeb/transforms/flatcss.py +++ b/src/calibre/ebooks/oeb/transforms/flatcss.py @@ -336,7 +336,7 @@ class CSSFlattener: except: sbase = 12.0 self.oeb.logger.info( - "Source base font size is %0.05fpt" % sbase) + 'Source base font size is %0.05fpt' % sbase) return sbase def clean_edges(self, cssdict, style, fsize): @@ -344,7 +344,7 @@ class CSSFlattener: dlineh = self.lineh for kind in ('margin', 'padding'): for edge in ('bottom', 'top'): - property = f"{kind}-{edge}" + property = f'{kind}-{edge}' if property not in cssdict: continue if '%' in cssdict[property]: @@ -353,7 +353,7 @@ class CSSFlattener: if value == 0 or not isinstance(value, numbers.Number): continue if value <= slineh: - cssdict[property] = "%0.5fem" % (dlineh / fsize) + cssdict[property] = '%0.5fem' % (dlineh / fsize) else: try: value = round(value / slineh) * dlineh @@ -361,7 +361,7 @@ class CSSFlattener: self.oeb.logger.warning( 'Invalid length:', value) value = 0.0 - cssdict[property] = "%0.5fem" % (value / fsize) + cssdict[property] = '%0.5fem' % (value / fsize) def flatten_node(self, node, stylizer, names, styles, pseudo_styles, psize, item_id, recurse=True): if not isinstance(node.tag, string_or_bytes) or namespace(node.tag) not in (XHTML_NS, SVG_NS): @@ -388,7 +388,7 @@ class CSSFlattener: if 'margin-left' not in cssdict and 'margin-right' not in cssdict: cssdict['margin-left'] = cssdict['margin-right'] = 'auto' else: - for table in node.iterchildren(XHTML("table")): + for table in node.iterchildren(XHTML('table')): ts = stylizer.style(table) if ts.get('margin-left') is None and ts.get('margin-right') is None: ts.set('margin-left', 'auto') @@ -481,7 +481,7 @@ class CSSFlattener: elif 'font-size' in cssdict or tag == 'body': fsize = self.fmap[font_size] try: - cssdict['font-size'] = "%0.5fem" % (fsize / psize) + cssdict['font-size'] = '%0.5fem' % (fsize / psize) except ZeroDivisionError: cssdict['font-size'] = '%.1fpt'%fsize psize = fsize @@ -521,7 +521,7 @@ class CSSFlattener: cssdict['vertical-align'] = 'super' if self.lineh and 'line-height' not in cssdict and tag != 'html': lineh = self.lineh / psize - cssdict['line-height'] = "%0.5fem" % lineh + cssdict['line-height'] = '%0.5fem' % lineh if (self.context.remove_paragraph_spacing or self.context.insert_blank_line) and tag in ('p', 'div'): if item_id != 'calibre_jacket' or self.context.output_profile.name == 'Kindle': @@ -534,7 +534,7 @@ class CSSFlattener: indent_size = self.context.remove_paragraph_spacing_indent_size keep_indents = indent_size < 0.0 if (self.context.remove_paragraph_spacing and not keep_indents and cssdict.get('text-align', None) not in ('center', 'right')): - cssdict['text-indent'] = "%1.1fem" % indent_size + cssdict['text-indent'] = '%1.1fem' % indent_size pseudo_classes = style.pseudo_classes(self.filter_css) if cssdict or pseudo_classes: @@ -659,7 +659,7 @@ class CSSFlattener: stylizer.page_rule['margin-bottom'] = '%gpt'%\ float(self.context.margin_bottom) items = sorted(stylizer.page_rule.items()) - css = ';\n'.join(f"{key}: {val}" for key, val in items) + css = ';\n'.join(f'{key}: {val}' for key, val in items) css = ('@page {\n%s\n}\n'%css) if items else '' rules = [css_text(r) for r in unique_font_face_rules(*stylizer.font_face_rules, *rules_in(self.embed_font_rules))] raw = '\n\n'.join(rules) @@ -707,7 +707,7 @@ class CSSFlattener: x = sorted(((k+':'+psel, v) for v, k in iteritems(styles))) items.extend(x) - css = ''.join(f".{key} {{\n{val};\n}}\n\n" for key, val in items) + css = ''.join(f'.{key} {{\n{val};\n}}\n\n' for key, val in items) href = self.replace_css(css) global_css = self.collect_global_css() diff --git a/src/calibre/ebooks/oeb/transforms/htmltoc.py b/src/calibre/ebooks/oeb/transforms/htmltoc.py index bb93a58d61..b68a0b52ea 100644 --- a/src/calibre/ebooks/oeb/transforms/htmltoc.py +++ b/src/calibre/ebooks/oeb/transforms/htmltoc.py @@ -13,7 +13,7 @@ __all__ = ['HTMLTOCAdder'] DEFAULT_TITLE = __('Table of Contents') STYLE_CSS = { - 'nested': """ + 'nested': ''' .calibre_toc_header { text-align: center; } @@ -27,9 +27,9 @@ STYLE_CSS = { .calibre_toc_block .calibre_toc_block .calibre_toc_block { margin-left: 3.6em; } -""", +''', - 'centered': """ + 'centered': ''' .calibre_toc_header { text-align: center; } @@ -39,7 +39,7 @@ STYLE_CSS = { body > .calibre_toc_block { margin-top: 1.2em; } -""" +''' } diff --git a/src/calibre/ebooks/oeb/transforms/jacket.py b/src/calibre/ebooks/oeb/transforms/jacket.py index 9e5932c191..1f0814994e 100644 --- a/src/calibre/ebooks/oeb/transforms/jacket.py +++ b/src/calibre/ebooks/oeb/transforms/jacket.py @@ -174,7 +174,7 @@ def get_rating(rating, rchar, e_rchar): if num < 1: return ans - ans = ("%s%s") % (rchar * int(num), e_rchar * (5 - int(num))) + ans = ('%s%s') % (rchar * int(num), e_rchar * (5 - int(num))) return ans @@ -389,10 +389,10 @@ def render_jacket(mi, output_profile, pass if False: - print("Custom column values available in jacket template:") + print('Custom column values available in jacket template:') for key in args.keys(): if key.startswith('_') and not key.endswith('_label'): - print(" {}: {}".format('#' + key[1:], args[key])) + print(' {}: {}'.format('#' + key[1:], args[key])) # Used in the comment describing use of custom columns in templates # Don't change this unless you also change it in template.xhtml diff --git a/src/calibre/ebooks/oeb/transforms/manglecase.py b/src/calibre/ebooks/oeb/transforms/manglecase.py index 7df2d9e330..aebd76a930 100644 --- a/src/calibre/ebooks/oeb/transforms/manglecase.py +++ b/src/calibre/ebooks/oeb/transforms/manglecase.py @@ -14,12 +14,12 @@ from calibre.utils.icu import title_case as icu_title from calibre.utils.icu import upper as icu_upper from polyglot.builtins import string_or_bytes -CASE_MANGLER_CSS = """ +CASE_MANGLER_CSS = ''' .calibre_lowercase { font-variant: normal; font-size: 0.65em; } -""" +''' TEXT_TRANSFORMS = {'capitalize', 'uppercase', 'lowercase'} diff --git a/src/calibre/ebooks/oeb/transforms/rasterize.py b/src/calibre/ebooks/oeb/transforms/rasterize.py index 6b2d583cbc..ce56c017c1 100644 --- a/src/calibre/ebooks/oeb/transforms/rasterize.py +++ b/src/calibre/ebooks/oeb/transforms/rasterize.py @@ -49,7 +49,7 @@ def rasterize_svg(data=None, sizes=(), width=0, height=0, print=None, fmt='PNG', if print is not None: print(f'Rasterizing SVG to {size.width()} x {size.height()}') image = QImage(size, QImage.Format.Format_ARGB32_Premultiplied) - image.fill(QColor("white").rgb()) + image.fill(QColor('white').rgb()) painter = QPainter(image) svg.render(painter) painter.end() @@ -219,7 +219,7 @@ class SVGRasterizer: logger.info('Rasterizing %r to %dx%d' % (svgitem.href, size.width(), size.height())) image = QImage(size, QImage.Format.Format_ARGB32_Premultiplied) - image.fill(QColor("white").rgb()) + image.fill(QColor('white').rgb()) painter = QPainter(image) svg.render(painter) painter.end() diff --git a/src/calibre/ebooks/oeb/transforms/structure.py b/src/calibre/ebooks/oeb/transforms/structure.py index f715277ade..a816e4aaf8 100644 --- a/src/calibre/ebooks/oeb/transforms/structure.py +++ b/src/calibre/ebooks/oeb/transforms/structure.py @@ -132,7 +132,7 @@ class DetectStructure: self.log('Setting start reading at position to %s in %s'%( self.opts.start_reading_at, item.href)) return - self.log.warn("Failed to find start reading at position: %s"% + self.log.warn('Failed to find start reading at position: %s'% self.opts.start_reading_at) def get_toc_parts_for_xpath(self, expr): diff --git a/src/calibre/ebooks/oeb/writer.py b/src/calibre/ebooks/oeb/writer.py index b30138101b..4d0108c4d6 100644 --- a/src/calibre/ebooks/oeb/writer.py +++ b/src/calibre/ebooks/oeb/writer.py @@ -15,10 +15,10 @@ __all__ = ['OEBWriter'] class OEBWriter: DEFAULT_PROFILE = 'PRS505' - """Default renderer profile for content written with this Writer.""" + '''Default renderer profile for content written with this Writer.''' TRANSFORMS = [] - """List of transforms to apply to content written with this Writer.""" + '''List of transforms to apply to content written with this Writer.''' def __init__(self, version='2.0', page_map=False, pretty_print=False): self.version = version @@ -27,9 +27,9 @@ class OEBWriter: @classmethod def config(cls, cfg): - """Add any book-writing options to the :class:`Config` object + '''Add any book-writing options to the :class:`Config` object :param:`cfg`. - """ + ''' oeb = cfg.add_group('oeb', _('OPF/NCX/etc. generation options.')) versions = ['1.2', '2.0'] oeb('opf_version', ['--opf-version'], default='2.0', choices=versions, @@ -41,7 +41,7 @@ class OEBWriter: @classmethod def generate(cls, opts): - """Generate a Writer instance from command-line options.""" + '''Generate a Writer instance from command-line options.''' version = opts.opf_version page_map = opts.adobe_page_map pretty_print = opts.pretty_print @@ -49,10 +49,10 @@ class OEBWriter: pretty_print=pretty_print) def __call__(self, oeb, path): - """ + ''' Write the book in the :class:`OEBBook` object :param:`oeb` to a folder at :param:`path`. - """ + ''' version = int(self.version[0]) opfname = None if os.path.splitext(path)[1].lower() == '.opf': @@ -69,7 +69,7 @@ class OEBWriter: elif version == 2: metadata = oeb.to_opf2(page_map=self.page_map) else: - raise OEBError("Unrecognized OPF version %r" % self.version) + raise OEBError('Unrecognized OPF version %r' % self.version) pretty_print = self.pretty_print for mime, (href, data) in metadata.items(): if opfname and mime == OPF_MIME: diff --git a/src/calibre/ebooks/pdb/haodoo/reader.py b/src/calibre/ebooks/pdb/haodoo/reader.py index 945efa7cdd..0bbca6a38f 100644 --- a/src/calibre/ebooks/pdb/haodoo/reader.py +++ b/src/calibre/ebooks/pdb/haodoo/reader.py @@ -19,32 +19,32 @@ BPDB_IDENT = 'BOOKMTIT' UPDB_IDENT = 'BOOKMTIU' punct_table = { - "︵": "(", - "︶": ")", - "︷": "{", - "︸": "}", - "︹": "〔", - "︺": "〕", - "︻": "【", - "︼": "】", - "︗": "〖", - "︘": "〗", - "﹇": "[]", - "﹈": "[]", - "︽": "《", - "︾": "》", - "︿": "〈", - "﹀": "〉", - "﹁": "「", - "﹂": "」", - "﹃": "『", - "﹄": "』", - "|": "—", - "︙": "…", - "ⸯ": "~", - "│": "…", - "¦": "…", - " ": " ", + '︵': '(', + '︶': ')', + '︷': '{', + '︸': '}', + '︹': '〔', + '︺': '〕', + '︻': '【', + '︼': '】', + '︗': '〖', + '︘': '〗', + '﹇': '[]', + '﹈': '[]', + '︽': '《', + '︾': '》', + '︿': '〈', + '﹀': '〉', + '﹁': '「', + '﹂': '」', + '﹃': '『', + '﹄': '』', + '|': '—', + '︙': '…', + 'ⸯ': '~', + '│': '…', + '¦': '…', + ' ': ' ', } diff --git a/src/calibre/ebooks/pdf/html_writer.py b/src/calibre/ebooks/pdf/html_writer.py index 7478427323..20a1bab3c7 100644 --- a/src/calibre/ebooks/pdf/html_writer.py +++ b/src/calibre/ebooks/pdf/html_writer.py @@ -220,7 +220,7 @@ class UrlSchemeHandler(QWebEngineUrlSchemeHandler): if fail_code is None: fail_code = QWebEngineUrlRequestJob.Error.UrlNotFound rq.fail(fail_code) - print(f"Blocking FAKE_PROTOCOL request: {rq.requestUrl().toString()} with code: {fail_code}", file=sys.stderr) + print(f'Blocking FAKE_PROTOCOL request: {rq.requestUrl().toString()} with code: {fail_code}', file=sys.stderr) # }}} @@ -1103,7 +1103,7 @@ def add_maths_script(container): has_maths[name] = hm = check_for_maths(root) if not hm: continue - script = root.makeelement(XHTML('script'), type="text/javascript", src=f'{FAKE_PROTOCOL}://{FAKE_HOST}/mathjax/loader/pdf-mathjax-loader.js') + script = root.makeelement(XHTML('script'), type='text/javascript', src=f'{FAKE_PROTOCOL}://{FAKE_HOST}/mathjax/loader/pdf-mathjax-loader.js') script.set('async', 'async') script.set('data-mathjax-path', f'{FAKE_PROTOCOL}://{FAKE_HOST}/mathjax/data/') last_tag(root).append(script) @@ -1212,9 +1212,9 @@ def convert(opf_path, opts, metadata=None, output_path=None, log=default_log, co mult = -1 if i % 2 else 1 val = opts.pdf_odd_even_offset if abs(val) < min(margins.left, margins.right): - box = list(pdf_doc.get_page_box("CropBox", i)) + box = list(pdf_doc.get_page_box('CropBox', i)) box[0] += val * mult - pdf_doc.set_page_box("CropBox", i, *box) + pdf_doc.set_page_box('CropBox', i, *box) if cover_data: add_cover(pdf_doc, cover_data, page_layout, opts) diff --git a/src/calibre/ebooks/pdf/pdftohtml.py b/src/calibre/ebooks/pdf/pdftohtml.py index 5044c83af8..8b9e9a6a17 100644 --- a/src/calibre/ebooks/pdf/pdftohtml.py +++ b/src/calibre/ebooks/pdf/pdftohtml.py @@ -75,7 +75,7 @@ def pdftohtml(output_dir, pdf_path, no_images, as_xml=False): if ret != 0: raise ConversionError('pdftohtml failed with return code: %d\n%s' % (ret, out)) if out: - prints("pdftohtml log:") + prints('pdftohtml log:') prints(out) if not os.path.exists(index) or os.stat(index).st_size < 100: raise DRMError() @@ -84,7 +84,7 @@ def pdftohtml(output_dir, pdf_path, no_images, as_xml=False): with open(index, 'r+b') as i: raw = i.read().decode('utf-8', 'replace') raw = flip_images(raw) - raw = raw.replace('<head', '<!-- created by calibre\'s pdftohtml -->\n <head', 1) + raw = raw.replace('<head', "<!-- created by calibre's pdftohtml -->\n <head", 1) i.seek(0) i.truncate() # versions of pdftohtml >= 0.20 output self closing <br> tags, this diff --git a/src/calibre/ebooks/pdf/render/common.py b/src/calibre/ebooks/pdf/render/common.py index ff5e18d5f1..5bee9f9f8a 100644 --- a/src/calibre/ebooks/pdf/render/common.py +++ b/src/calibre/ebooks/pdf/render/common.py @@ -74,7 +74,7 @@ def serialize(o, stream): elif o is None: stream.write_raw(b'null') elif isinstance(o, datetime): - val = o.strftime("D:%Y%m%d%H%M%%02d%z")%min(59, o.second) + val = o.strftime('D:%Y%m%d%H%M%%02d%z')%min(59, o.second) if datetime.tzinfo is not None: val = "(%s'%s')"%(val[:-2], val[-2:]) stream.write(val.encode('ascii')) diff --git a/src/calibre/ebooks/pdf/render/graphics.py b/src/calibre/ebooks/pdf/render/graphics.py index 70b43a6224..a55bb6c5a8 100644 --- a/src/calibre/ebooks/pdf/render/graphics.py +++ b/src/calibre/ebooks/pdf/render/graphics.py @@ -70,154 +70,154 @@ class TilingPattern(Stream): class QtPattern(TilingPattern): qt_patterns = ( # {{{ - "0 J\n" - "6 w\n" - "[] 0 d\n" - "4 0 m\n" - "4 8 l\n" - "0 4 m\n" - "8 4 l\n" - "S\n", # Dense1Pattern + '0 J\n' + '6 w\n' + '[] 0 d\n' + '4 0 m\n' + '4 8 l\n' + '0 4 m\n' + '8 4 l\n' + 'S\n', # Dense1Pattern - "0 J\n" - "2 w\n" - "[6 2] 1 d\n" - "0 0 m\n" - "0 8 l\n" - "8 0 m\n" - "8 8 l\n" - "S\n" - "[] 0 d\n" - "2 0 m\n" - "2 8 l\n" - "6 0 m\n" - "6 8 l\n" - "S\n" - "[6 2] -3 d\n" - "4 0 m\n" - "4 8 l\n" - "S\n", # Dense2Pattern + '0 J\n' + '2 w\n' + '[6 2] 1 d\n' + '0 0 m\n' + '0 8 l\n' + '8 0 m\n' + '8 8 l\n' + 'S\n' + '[] 0 d\n' + '2 0 m\n' + '2 8 l\n' + '6 0 m\n' + '6 8 l\n' + 'S\n' + '[6 2] -3 d\n' + '4 0 m\n' + '4 8 l\n' + 'S\n', # Dense2Pattern - "0 J\n" - "2 w\n" - "[6 2] 1 d\n" - "0 0 m\n" - "0 8 l\n" - "8 0 m\n" - "8 8 l\n" - "S\n" - "[2 2] -1 d\n" - "2 0 m\n" - "2 8 l\n" - "6 0 m\n" - "6 8 l\n" - "S\n" - "[6 2] -3 d\n" - "4 0 m\n" - "4 8 l\n" - "S\n", # Dense3Pattern + '0 J\n' + '2 w\n' + '[6 2] 1 d\n' + '0 0 m\n' + '0 8 l\n' + '8 0 m\n' + '8 8 l\n' + 'S\n' + '[2 2] -1 d\n' + '2 0 m\n' + '2 8 l\n' + '6 0 m\n' + '6 8 l\n' + 'S\n' + '[6 2] -3 d\n' + '4 0 m\n' + '4 8 l\n' + 'S\n', # Dense3Pattern - "0 J\n" - "2 w\n" - "[2 2] 1 d\n" - "0 0 m\n" - "0 8 l\n" - "8 0 m\n" - "8 8 l\n" - "S\n" - "[2 2] -1 d\n" - "2 0 m\n" - "2 8 l\n" - "6 0 m\n" - "6 8 l\n" - "S\n" - "[2 2] 1 d\n" - "4 0 m\n" - "4 8 l\n" - "S\n", # Dense4Pattern + '0 J\n' + '2 w\n' + '[2 2] 1 d\n' + '0 0 m\n' + '0 8 l\n' + '8 0 m\n' + '8 8 l\n' + 'S\n' + '[2 2] -1 d\n' + '2 0 m\n' + '2 8 l\n' + '6 0 m\n' + '6 8 l\n' + 'S\n' + '[2 2] 1 d\n' + '4 0 m\n' + '4 8 l\n' + 'S\n', # Dense4Pattern - "0 J\n" - "2 w\n" - "[2 6] -1 d\n" - "0 0 m\n" - "0 8 l\n" - "8 0 m\n" - "8 8 l\n" - "S\n" - "[2 2] 1 d\n" - "2 0 m\n" - "2 8 l\n" - "6 0 m\n" - "6 8 l\n" - "S\n" - "[2 6] 3 d\n" - "4 0 m\n" - "4 8 l\n" - "S\n", # Dense5Pattern + '0 J\n' + '2 w\n' + '[2 6] -1 d\n' + '0 0 m\n' + '0 8 l\n' + '8 0 m\n' + '8 8 l\n' + 'S\n' + '[2 2] 1 d\n' + '2 0 m\n' + '2 8 l\n' + '6 0 m\n' + '6 8 l\n' + 'S\n' + '[2 6] 3 d\n' + '4 0 m\n' + '4 8 l\n' + 'S\n', # Dense5Pattern - "0 J\n" - "2 w\n" - "[2 6] -1 d\n" - "0 0 m\n" - "0 8 l\n" - "8 0 m\n" - "8 8 l\n" - "S\n" - "[2 6] 3 d\n" - "4 0 m\n" - "4 8 l\n" - "S\n", # Dense6Pattern + '0 J\n' + '2 w\n' + '[2 6] -1 d\n' + '0 0 m\n' + '0 8 l\n' + '8 0 m\n' + '8 8 l\n' + 'S\n' + '[2 6] 3 d\n' + '4 0 m\n' + '4 8 l\n' + 'S\n', # Dense6Pattern - "0 J\n" - "2 w\n" - "[2 6] -1 d\n" - "0 0 m\n" - "0 8 l\n" - "8 0 m\n" - "8 8 l\n" - "S\n", # Dense7Pattern + '0 J\n' + '2 w\n' + '[2 6] -1 d\n' + '0 0 m\n' + '0 8 l\n' + '8 0 m\n' + '8 8 l\n' + 'S\n', # Dense7Pattern - "1 w\n" - "0 4 m\n" - "8 4 l\n" - "S\n", # HorPattern + '1 w\n' + '0 4 m\n' + '8 4 l\n' + 'S\n', # HorPattern - "1 w\n" - "4 0 m\n" - "4 8 l\n" - "S\n", # VerPattern + '1 w\n' + '4 0 m\n' + '4 8 l\n' + 'S\n', # VerPattern - "1 w\n" - "4 0 m\n" - "4 8 l\n" - "0 4 m\n" - "8 4 l\n" - "S\n", # CrossPattern + '1 w\n' + '4 0 m\n' + '4 8 l\n' + '0 4 m\n' + '8 4 l\n' + 'S\n', # CrossPattern - "1 w\n" - "-1 5 m\n" - "5 -1 l\n" - "3 9 m\n" - "9 3 l\n" - "S\n", # BDiagPattern + '1 w\n' + '-1 5 m\n' + '5 -1 l\n' + '3 9 m\n' + '9 3 l\n' + 'S\n', # BDiagPattern - "1 w\n" - "-1 3 m\n" - "5 9 l\n" - "3 -1 m\n" - "9 5 l\n" - "S\n", # FDiagPattern + '1 w\n' + '-1 3 m\n' + '5 9 l\n' + '3 -1 m\n' + '9 5 l\n' + 'S\n', # FDiagPattern - "1 w\n" - "-1 3 m\n" - "5 9 l\n" - "3 -1 m\n" - "9 5 l\n" - "-1 5 m\n" - "5 -1 l\n" - "3 9 m\n" - "9 3 l\n" - "S\n", # DiagCrossPattern + '1 w\n' + '-1 3 m\n' + '5 9 l\n' + '3 -1 m\n' + '9 5 l\n' + '-1 5 m\n' + '5 -1 l\n' + '3 9 m\n' + '9 3 l\n' + 'S\n', # DiagCrossPattern ) # }}} def __init__(self, pattern_num, matrix): diff --git a/src/calibre/ebooks/readability/cleaners.py b/src/calibre/ebooks/readability/cleaners.py index cd52fe2580..1256c01360 100644 --- a/src/calibre/ebooks/readability/cleaners.py +++ b/src/calibre/ebooks/readability/cleaners.py @@ -10,12 +10,12 @@ bad_attrs = ['width', 'height', 'style', '[-a-z]*color', 'background[-a-z]*', 'o single_quoted = "'[^']+'" double_quoted = '"[^"]+"' non_space = '[^ "\'>]+' -htmlstrip = re.compile("<" # open - "([^>]+) " # prefix - "(?:%s) *" % ('|'.join(bad_attrs),) + # undesirable attributes +htmlstrip = re.compile('<' # open + '([^>]+) ' # prefix + '(?:%s) *' % ('|'.join(bad_attrs),) + # undesirable attributes f'= *(?:{non_space}|{single_quoted}|{double_quoted})' + # value - "([^>]*)" # postfix - ">" # end + '([^>]*)' # postfix + '>' # end , re.I) @@ -28,8 +28,8 @@ def clean_attributes(html): def normalize_spaces(s): if not s: return '' - """replace any sequence of whitespace - characters with a single space""" + '''replace any sequence of whitespace + characters with a single space''' return ' '.join(s.split()) diff --git a/src/calibre/ebooks/readability/debug.py b/src/calibre/ebooks/readability/debug.py index 07b3f67dd1..3366eecfc5 100644 --- a/src/calibre/ebooks/readability/debug.py +++ b/src/calibre/ebooks/readability/debug.py @@ -9,7 +9,7 @@ uids = {} def describe(node, depth=2): if not hasattr(node, 'tag'): - return "[%s]" % type(node) + return '[%s]' % type(node) name = node.tag if node.get('id', ''): name += '#'+node.get('id') @@ -22,7 +22,7 @@ def describe(node, depth=2): uid = uids[node] = len(uids)+1 else: uid = uids.get(node) - name += "%02d" % (uid) + name += '%02d' % (uid) if depth and node.getparent() is not None: return name+' - '+describe(node.getparent(), depth-1) return name diff --git a/src/calibre/ebooks/readability/readability.py b/src/calibre/ebooks/readability/readability.py index b9085e90be..2aebf16d5d 100644 --- a/src/calibre/ebooks/readability/readability.py +++ b/src/calibre/ebooks/readability/readability.py @@ -36,7 +36,7 @@ REGEXES = { def describe(node, depth=1): if not hasattr(node, 'tag'): - return "[%s]" % type(node) + return '[%s]' % type(node) name = node.tag if node.get('id', ''): name += '#'+node.get('id') @@ -67,7 +67,7 @@ def clean(text): def text_length(i): - return len(clean(i.text_content() or "")) + return len(clean(i.text_content() or '')) class Unparsable(ValueError): @@ -135,13 +135,13 @@ class Document: article = self.get_article(candidates, best_candidate) else: if ruthless: - self.log.debug("ruthless removal did not work. ") + self.log.debug('ruthless removal did not work. ') ruthless = False - self.debug("ended up stripping too much - going for a safer _parse") + self.debug('ended up stripping too much - going for a safer _parse') # try again continue else: - self.log.debug("Ruthless and lenient parsing did not work. Returning raw html") + self.log.debug('Ruthless and lenient parsing did not work. Returning raw html') article = self.html.find('body') if article is None: article = self.html @@ -175,9 +175,9 @@ class Document: if sibling in self.keep_elements: append = True - if sibling.tag == "p": + if sibling.tag == 'p': link_density = self.get_link_density(sibling) - node_content = sibling.text or "" + node_content = sibling.text or '' node_length = len(node_content) if node_length > 80 and link_density < 0.25: @@ -195,7 +195,7 @@ class Document: sorted_candidates = sorted(candidates.values(), key=lambda x: x['content_score'], reverse=True) for candidate in sorted_candidates[:5]: elem = candidate['elem'] - self.debug("Top 5 : {:6.3f} {}".format(candidate['content_score'], describe(elem))) + self.debug('Top 5 : {:6.3f} {}'.format(candidate['content_score'], describe(elem))) if len(sorted_candidates) == 0: return None @@ -205,7 +205,7 @@ class Document: def get_link_density(self, elem): link_length = 0 - for i in elem.findall(".//a"): + for i in elem.findall('.//a'): link_length += text_length(i) # if len(elem.findall(".//div") or elem.findall(".//p")): # link_length = link_length @@ -218,13 +218,13 @@ class Document: # self.debug(str([describe(node) for node in self.tags(self.html, "div")])) ordered = [] - for elem in self.tags(self.html, "p", "pre", "td"): + for elem in self.tags(self.html, 'p', 'pre', 'td'): parent_node = elem.getparent() if parent_node is None: continue grand_parent_node = parent_node.getparent() - inner_text = clean(elem.text_content() or "") + inner_text = clean(elem.text_content() or '') inner_text_len = len(inner_text) # If this paragraph is less than 25 characters, don't even count it. @@ -256,7 +256,7 @@ class Document: candidate = candidates[elem] ld = self.get_link_density(elem) score = candidate['content_score'] - self.debug(f"Candid: {score:6.3f} {describe(elem)} link density {ld:.3f} -> {score*(1-ld):6.3f}") + self.debug(f'Candid: {score:6.3f} {describe(elem)} link density {ld:.3f} -> {score*(1-ld):6.3f}') candidate['content_score'] *= (1 - ld) return candidates @@ -282,13 +282,13 @@ class Document: def score_node(self, elem): content_score = self.class_weight(elem) name = elem.tag.lower() - if name == "div": + if name == 'div': content_score += 5 - elif name in ["pre", "td", "blockquote"]: + elif name in ['pre', 'td', 'blockquote']: content_score += 3 - elif name in ["address", "ol", "ul", "dl", "dd", "dt", "li", "form"]: + elif name in ['address', 'ol', 'ul', 'dl', 'dd', 'dt', 'li', 'form']: content_score -= 3 - elif name in ["h1", "h2", "h3", "h4", "h5", "h6", "th"]: + elif name in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'th']: content_score -= 5 return { 'content_score': content_score, @@ -303,10 +303,10 @@ class Document: for elem in self.html.iter(): if elem in self.keep_elements: continue - s = "{} {}".format(elem.get('class', ''), elem.get('id', '')) + s = '{} {}'.format(elem.get('class', ''), elem.get('id', '')) # self.debug(s) if REGEXES['unlikelyCandidatesRe'].search(s) and (not REGEXES['okMaybeItsACandidateRe'].search(s)) and elem.tag != 'body': - self.debug("Removing unlikely candidate - %s" % describe(elem)) + self.debug('Removing unlikely candidate - %s' % describe(elem)) elem.drop_tree() def transform_misused_divs_into_paragraphs(self): @@ -314,7 +314,7 @@ class Document: # transform <div>s that do not contain other block elements into <p>s if not REGEXES['divToPElementsRe'].search(str(''.join(map(tounicode, list(elem))))): # self.debug("Altering %s to p" % (describe(elem))) - elem.tag = "p" + elem.tag = 'p' # print("Fixed element "+describe(elem)) for elem in self.tags(self.html, 'div'): @@ -346,15 +346,15 @@ class Document: def sanitize(self, node, candidates): MIN_LEN = self.options.get('min_text_length', self.TEXT_LENGTH_THRESHOLD) - for header in self.tags(node, "h1", "h2", "h3", "h4", "h5", "h6"): + for header in self.tags(node, 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'): if self.class_weight(header) < 0 or self.get_link_density(header) > 0.33: header.drop_tree() - for elem in self.tags(node, "form", "iframe", "textarea"): + for elem in self.tags(node, 'form', 'iframe', 'textarea'): elem.drop_tree() allowed = {} # Conditionally clean <table>s, <ul>s, and <div>s - for el in self.reverse_tags(node, "table", "ul", "div"): + for el in self.reverse_tags(node, 'table', 'ul', 'div'): if el in allowed or el in self.keep_elements: continue weight = self.class_weight(el) @@ -366,14 +366,14 @@ class Document: tag = el.tag if weight + content_score < 0: - self.debug("Cleaned %s with score %6.3f and weight %-3s" % + self.debug('Cleaned %s with score %6.3f and weight %-3s' % (describe(el), content_score, weight, )) el.drop_tree() - elif el.text_content().count(",") < 10: + elif el.text_content().count(',') < 10: counts = {} for kind in ['p', 'img', 'li', 'a', 'embed', 'input']: counts[kind] = len(el.findall('.//%s' %kind)) - counts["li"] -= 100 + counts['li'] -= 100 content_length = text_length(el) # Count the text length excluding any surrounding whitespace link_density = self.get_link_density(el) @@ -390,30 +390,30 @@ class Document: # pweight = 0 # pname = "no parent" to_remove = False - reason = "" + reason = '' # if el.tag == 'div' and counts["img"] >= 1: # continue - if counts["p"] and counts["img"] > counts["p"]: - reason = "too many images (%s)" % counts["img"] + if counts['p'] and counts['img'] > counts['p']: + reason = 'too many images (%s)' % counts['img'] to_remove = True - elif counts["li"] > counts["p"] and tag != "ul" and tag != "ol": - reason = "more <li>s than <p>s" + elif counts['li'] > counts['p'] and tag != 'ul' and tag != 'ol': + reason = 'more <li>s than <p>s' to_remove = True - elif counts["input"] > (counts["p"] / 3): - reason = "less than 3x <p>s than <input>s" + elif counts['input'] > (counts['p'] / 3): + reason = 'less than 3x <p>s than <input>s' to_remove = True - elif content_length < (MIN_LEN) and (counts["img"] == 0 or counts["img"] > 2): - reason = "too short content length %s without a single image" % content_length + elif content_length < (MIN_LEN) and (counts['img'] == 0 or counts['img'] > 2): + reason = 'too short content length %s without a single image' % content_length to_remove = True elif weight < 25 and link_density > 0.2: - reason = f"too many links {link_density:.3f} for its weight {weight}" + reason = f'too many links {link_density:.3f} for its weight {weight}' to_remove = True elif weight >= 25 and link_density > 0.5: - reason = f"too many links {link_density:.3f} for its weight {weight}" + reason = f'too many links {link_density:.3f} for its weight {weight}' to_remove = True - elif (counts["embed"] == 1 and content_length < 75) or counts["embed"] > 1: - reason = "<embed>s with too short content length, or too many <embed>s" + elif (counts['embed'] == 1 and content_length < 75) or counts['embed'] > 1: + reason = '<embed>s with too short content length, or too many <embed>s' to_remove = True # if el.tag == 'div' and counts['img'] >= 1 and to_remove: # imgs = el.findall('.//img') @@ -457,12 +457,12 @@ class Document: # self.debug(str(siblings)) if siblings and sum(siblings) > 1000 : to_remove = False - self.debug("Allowing %s" % describe(el)) - for desnode in self.tags(el, "table", "ul", "div"): + self.debug('Allowing %s' % describe(el)) + for desnode in self.tags(el, 'table', 'ul', 'div'): allowed[desnode] = True if to_remove: - self.debug("Cleaned %6.3f %s with weight %s cause it has %s." % + self.debug('Cleaned %6.3f %s with weight %s cause it has %s.' % (content_score, describe(el), weight, reason)) # print(tounicode(el)) # self.debug("pname %s pweight %.3f" %(pname, pweight)) diff --git a/src/calibre/ebooks/render_html.py b/src/calibre/ebooks/render_html.py index 08272fbec0..cf4baa9c94 100644 --- a/src/calibre/ebooks/render_html.py +++ b/src/calibre/ebooks/render_html.py @@ -79,7 +79,7 @@ class UrlSchemeHandler(QWebEngineUrlSchemeHandler): if fail_code is None: fail_code = QWebEngineUrlRequestJob.Error.UrlNotFound rq.fail(fail_code) - print(f"Blocking FAKE_PROTOCOL request: {rq.requestUrl().toString()} with code: {fail_code}", file=sys.stderr) + print(f'Blocking FAKE_PROTOCOL request: {rq.requestUrl().toString()} with code: {fail_code}', file=sys.stderr) class Render(QWebEnginePage): diff --git a/src/calibre/ebooks/rtf/preprocess.py b/src/calibre/ebooks/rtf/preprocess.py index 9d3412fc3a..2e9024a0de 100644 --- a/src/calibre/ebooks/rtf/preprocess.py +++ b/src/calibre/ebooks/rtf/preprocess.py @@ -5,13 +5,13 @@ __license__ = 'GPL v3' __copyright__ = '2010, Gerendi Sandor Attila' __docformat__ = 'restructuredtext en' -""" +''' RTF tokenizer and token parser. v.1.0 (1/17/2010) Author: Gerendi Sandor Attila At this point this will tokenize a RTF file then rebuild it from the tokens. In the process the UTF8 tokens are altered to be supported by the RTF2XML and also remain RTF specification compliant. -""" +''' class tokenDelimitatorStart(): @@ -96,10 +96,10 @@ class tokenBinN(): self.separator = separator def toRTF(self): - return "\\bin" + repr(len(self.data)) + self.separator + self.data + return '\\bin' + repr(len(self.data)) + self.separator + self.data def __repr__(self): - return "\\bin" + repr(len(self.data)) + self.separator + self.data + return '\\bin' + repr(len(self.data)) + self.separator + self.data class token8bitChar(): @@ -253,7 +253,7 @@ class RtfTokenParser(): result = [] for token in self.tokens: result.append(token.toRTF()) - return "".join(result) + return ''.join(result) class RtfTokenizer(): @@ -334,7 +334,7 @@ class RtfTokenizer(): controlWord = self.rtfData[tokenStart: tokenEnd] if tokenEnd < i: value = int(self.rtfData[tokenEnd: i]) - if isString(controlWord, "\\bin"): + if isString(controlWord, '\\bin'): i = i + value self.tokens.append(tokenBinN(self.rtfData[tokenStart:i], separator)) else: @@ -359,13 +359,13 @@ class RtfTokenizer(): result = [] for token in self.tokens: result.append(token.toRTF()) - return "".join(result) + return ''.join(result) -if __name__ == "__main__": +if __name__ == '__main__': import sys if len(sys.argv) < 2: - print("Usage %prog rtfFileToConvert") + print('Usage %prog rtfFileToConvert') sys.exit() with open(sys.argv[1], 'rb') as f: data = f.read() diff --git a/src/calibre/ebooks/rtf2xml/ParseRtf.py b/src/calibre/ebooks/rtf2xml/ParseRtf.py index 4c182370b9..cfcf8c8297 100644 --- a/src/calibre/ebooks/rtf2xml/ParseRtf.py +++ b/src/calibre/ebooks/rtf2xml/ParseRtf.py @@ -57,7 +57,7 @@ from calibre.ebooks.rtf2xml.old_rtf import OldRtf from . import open_for_read, open_for_write -""" +''' Here is an example script using the ParseRTF module directly #!/usr/bin/env python @@ -98,27 +98,27 @@ def Handle_Main(): sys.stderr.write(msg) except ParseRtf.RtfInvalidCodeException, msg: sys.stderr.write(msg) -""" +''' class InvalidRtfException(Exception): - """ + ''' handle invalid RTF - """ + ''' pass class RtfInvalidCodeException(Exception): - """ + ''' handle bugs in program - """ + ''' pass class ParseRtf: - """ + ''' Main class for controlling the rest of the parsing. - """ + ''' def __init__(self, in_file, @@ -164,7 +164,7 @@ class ParseRtf: self.__out_dir = out_dir self.__temp_dir = out_dir self.__dtd_path = dtd - self.__check_file(in_file,"file_to_parse") + self.__check_file(in_file,'file_to_parse') self.__char_data = char_data self.__debug_dir = deb_dir self.__check_dir(self.__temp_dir) @@ -186,12 +186,12 @@ class ParseRtf: self.__default_encoding = default_encoding def __check_file(self, the_file, type): - """Check to see if files exist""" + '''Check to see if files exist''' if hasattr(the_file, 'read'): return if the_file is None: - if type == "file_to_parse": - msg = "\nYou must provide a file for the script to work" + if type == 'file_to_parse': + msg = '\nYou must provide a file for the script to work' raise RtfInvalidCodeException(msg) elif os.path.exists(the_file): pass # do nothing @@ -200,12 +200,12 @@ class ParseRtf: raise RtfInvalidCodeException(msg) def __check_dir(self, the_dir): - """Check to see if directory exists""" + '''Check to see if directory exists''' if not the_dir : return dir_exists = os.path.isdir(the_dir) if not dir_exists: - msg = "\n%s is not a directory" % the_dir + msg = '\n%s is not a directory' % the_dir raise RtfInvalidCodeException(msg) return 1 @@ -228,7 +228,7 @@ class ParseRtf: ) copy_obj.set_dir(self.__debug_dir) copy_obj.remove_files() - copy_obj.copy_file(self.__temp_file, "original_file") + copy_obj.copy_file(self.__temp_file, 'original_file') # Function to check if bracket are well handled if self.__debug_dir or self.__run_level > 2: self.__check_brack_obj = check_brackets.CheckBrackets( @@ -591,8 +591,8 @@ class ParseRtf: self.__exit_level = num def __make_temp_file(self,file): - """Make a temporary file to parse""" - write_file="rtf_write_file" + '''Make a temporary file to parse''' + write_file='rtf_write_file' read_obj = file if hasattr(file, 'read') else open_for_read(file) with open_for_write(write_file) as write_obj: for line in read_obj: diff --git a/src/calibre/ebooks/rtf2xml/add_brackets.py b/src/calibre/ebooks/rtf2xml/add_brackets.py index 30b03d829c..f41df1c120 100644 --- a/src/calibre/ebooks/rtf2xml/add_brackets.py +++ b/src/calibre/ebooks/rtf2xml/add_brackets.py @@ -22,13 +22,13 @@ from . import open_for_read, open_for_write class AddBrackets: - """ + ''' Add brackets for old RTF. Logic: When control words without their own brackets are encountered and in the list of allowed words, this will add brackets to facilitate the treatment of the file - """ + ''' def __init__(self, in_file, bug_handler, @@ -87,9 +87,9 @@ class AddBrackets: ] def __initiate_values(self): - """ + ''' Init temp values - """ + ''' self.__state = 'before_body' self.__inline = {} self.__temp_group = [] @@ -97,15 +97,15 @@ class AddBrackets: self.__found_brackets = False def __before_body_func(self, line): - """ + ''' If we are before the body, not interest in changing anything - """ + ''' if self.__token_info == 'mi<mk<body-open_': self.__state = 'in_body' self.__write_obj.write(line) def __in_body_func(self, line): - """ + ''' Select what action to take in body: 1-At the end of the file close the braket if a bracket was opened This happens if there is achange @@ -114,7 +114,7 @@ class AddBrackets: 3-If an accepted control word is found put the line in a buffer then change state to after cw 4-Else simply write the line - """ + ''' if line == 'cb<nu<clos-brack<0001\n' and self.__open_bracket: self.__write_obj.write( 'cb<nu<clos-brack<0003\n' @@ -132,12 +132,12 @@ class AddBrackets: self.__write_obj.write(line) def __after_control_word_func(self, line): - """ + ''' After a cw either add next allowed cw to temporary list or change group and write it. If the token leading to an exit is an open bracket go to ignore otherwise goto in body - """ + ''' if self.__token_info in self.__accept: self.__temp_group.append(line) else: @@ -151,13 +151,13 @@ class AddBrackets: self.__state = 'in_body' def __write_group(self): - """ + ''' Write a temporary group after accepted control words end But this is mostly useless in my opinion as there is no list of rejected cw This may be a way to implement future old rtf processing for cw Utility: open a group to just put brackets but why be so complicated? Scheme: open brackets, write cw then go to body and back with cw after - """ + ''' if self.__open_bracket: self.__write_obj.write( 'cb<nu<clos-brack<0003\n' @@ -174,36 +174,36 @@ class AddBrackets: self.__temp_group = [] def __change_permanent_group(self): - """ + ''' Use temp group to change permanent group If the control word is not accepted remove it What is the interest as it is build to accept only accepted cw in __after_control_word_func? - """ + ''' self.__inline = {line[:16] : line[20:-1] for line in self.__temp_group\ # Is this really necessary? if line[:16] in self.__accept} def __ignore_func(self, line): - """ + ''' Just copy data inside of RTF brackets already here. - """ + ''' self.__write_obj.write(line) if self.__token_info == 'cb<nu<clos-brack'\ and self.__cb_count == self.__ignore_count: self.__state = 'in_body' def __check_brackets(self, in_file): - """ + ''' Return True if brackets match - """ + ''' check_brack_obj = check_brackets.CheckBrackets(file=in_file) return check_brack_obj.check_brackets()[0] def add_brackets(self): - """ - """ + ''' + ''' self.__initiate_values() with open_for_read(self.__file) as read_obj: with open_for_write(self.__write_to) as self.__write_obj: @@ -223,7 +223,7 @@ class AddBrackets: if self.__check_brackets(self.__write_to): copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "add_brackets.data") + copy_obj.copy_file(self.__write_to, 'add_brackets.data') copy_obj.rename(self.__write_to, self.__file) else: if self.__run_level > 0: diff --git a/src/calibre/ebooks/rtf2xml/body_styles.py b/src/calibre/ebooks/rtf2xml/body_styles.py index 65d784513a..c1c77167fa 100644 --- a/src/calibre/ebooks/rtf2xml/body_styles.py +++ b/src/calibre/ebooks/rtf2xml/body_styles.py @@ -17,16 +17,16 @@ from calibre.ptempfile import better_mktemp from . import open_for_read, open_for_write -""" +''' Simply write the list of strings after style table -""" +''' class BodyStyles: - """ + ''' Insert table data for tables. Logic: - """ + ''' def __init__(self, in_file, @@ -54,8 +54,8 @@ class BodyStyles: # self.__write_to = 'table_info.data' def insert_info(self): - """ - """ + ''' + ''' read_obj = open_for_read(self.__file) self.__write_obj = open_for_write(self.__write_to) line_to_read = 1 @@ -80,6 +80,6 @@ class BodyStyles: self.__write_obj.close() copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "body_styles.data") + copy_obj.copy_file(self.__write_to, 'body_styles.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/border_parse.py b/src/calibre/ebooks/rtf2xml/border_parse.py index 2bf4c001f5..6c03177e26 100644 --- a/src/calibre/ebooks/rtf2xml/border_parse.py +++ b/src/calibre/ebooks/rtf2xml/border_parse.py @@ -14,9 +14,9 @@ import sys class BorderParse: - """ + ''' Parse a border line and return a dictionary of attributes and values - """ + ''' def __init__(self): # cw<bd<bor-t-r-hi<nu<true @@ -75,13 +75,13 @@ class BorderParse: } def parse_border(self, line): - """ + ''' Requires: line -- line with border definition in it Returns: ? Logic: - """ + ''' border_dict = {} border_style_dict = {} border_style_list = [] diff --git a/src/calibre/ebooks/rtf2xml/char_set.py b/src/calibre/ebooks/rtf2xml/char_set.py index 7ea445cac4..a02f6f9f74 100644 --- a/src/calibre/ebooks/rtf2xml/char_set.py +++ b/src/calibre/ebooks/rtf2xml/char_set.py @@ -1,4 +1,4 @@ -char_set = """ +char_set = ''' <ms_standard> NON-BREAKING HYPEHN:_:8290:‑ LEFT DOUBLE QUOTATION MARK:ldblquote:8220:“ @@ -16705,4 +16705,4 @@ HORIZONTAL TABULATION:TAB :9: NULL:\xef:0:&#xnull; </single_set> </unused> -""" +''' diff --git a/src/calibre/ebooks/rtf2xml/check_brackets.py b/src/calibre/ebooks/rtf2xml/check_brackets.py index 355af163b4..c2b883faa1 100644 --- a/src/calibre/ebooks/rtf2xml/check_brackets.py +++ b/src/calibre/ebooks/rtf2xml/check_brackets.py @@ -16,7 +16,7 @@ from . import open_for_read class CheckBrackets: - """Check that brackets match up""" + '''Check that brackets match up''' def __init__(self, bug_handler=None, file=None): self.__file=file @@ -55,7 +55,7 @@ class CheckBrackets: return (False, "closed bracket doesn't match, line %s" % line_count) if self.__bracket_count != 0: - msg = ('At end of file open and closed brackets don\'t match\n' + msg = ("At end of file open and closed brackets don't match\n" 'total number of brackets is %s') % self.__bracket_count return (False, msg) - return (True, "Brackets match!") + return (True, 'Brackets match!') diff --git a/src/calibre/ebooks/rtf2xml/colors.py b/src/calibre/ebooks/rtf2xml/colors.py index 7f0fa81c7b..4cd666e4dd 100644 --- a/src/calibre/ebooks/rtf2xml/colors.py +++ b/src/calibre/ebooks/rtf2xml/colors.py @@ -21,9 +21,9 @@ from . import open_for_read, open_for_write class Colors: - """ + ''' Change lines with color info from color numbers to the actual color names. - """ + ''' def __init__(self, in_file, @@ -49,9 +49,9 @@ class Colors: self.__run_level = run_level def __initiate_values(self): - """ + ''' Initiate all values. - """ + ''' self.__color_dict = {} self.__state = 'before_color_table' self.__state_dict = { @@ -69,7 +69,7 @@ class Colors: # cw<bd<bor-par-to<nu<bdr-hair__|bdr-li-wid:0.50|bdr-sp-wid:1.00|bdr-color_:2 def __before_color_func(self, line): - """ + ''' Requires: line Returns: @@ -78,21 +78,21 @@ class Colors: Check to see if the line marks the beginning of the color table. If so, change states. Always print out the line. - """ + ''' # mi<mk<clrtbl-beg if self.__token_info == 'mi<mk<clrtbl-beg': self.__state = 'in_color_table' self.__write_obj.write(line) def __default_color_func(self, line): - """ + ''' Requires: line Returns: nothing Logic: get the hex number from the line and add it to the color string. - """ + ''' hex_num = line[-3:-1] self.__color_string += hex_num @@ -120,7 +120,7 @@ class Colors: self.__color_string = '#' def __in_color_func(self, line): - """ + ''' Requires: line Returns: @@ -130,7 +130,7 @@ class Colors: change the state to after the color table. Otherwise, get a function by passing the self.__token_info to the state dictionary. - """ + ''' # mi<mk<clrtbl-beg # cw<ci<red_______<nu<00 if self.__token_info == 'mi<mk<clrtbl-end': @@ -145,14 +145,14 @@ class Colors: action(line) def __after_color_func(self, line): - """ + ''' Check the to see if it contains color info. If it does, extract the number and look up the hex value in the color dictionary. If the color dictionary has no key for the number, print out an error message. Otherwise, print out the line. Added Oct 10, 2003 If the number is 0, that indicates no color - """ + ''' # cw<ci<font-color<nu<2 if self.__token_info == 'cw<ci<font-color': hex_num = int(line[20:-1]) @@ -166,7 +166,7 @@ class Colors: if the_index > -1: line = re.sub(self.__line_color_exp, self.__sub_from_line_color, line) self.__write_obj.write(line) - """ + ''' if num == 0: hex_num = 'false' else: @@ -187,7 +187,7 @@ class Colors: self.__write_obj.write( 'cw<ci<font-color<nu<%s\n' % hex_num ) - """ + ''' else: self.__write_obj.write(line) # cw<bd<bor-par-to<nu<bdr-hair__|bdr-li-wid:0.50|bdr-sp-wid:1.00|bdr-color_:2 @@ -198,7 +198,7 @@ class Colors: num = int(num) except ValueError: if self.__run_level > 3: - msg = 'can\'t make integer from string\n' + msg = "can't make integer from string\n" raise self.__bug_handler(msg) else: return 'bdr-color_:no-value' @@ -219,13 +219,13 @@ class Colors: return hex_num def __do_nothing_func(self, line): - """ + ''' Bad RTF will have text in the color table - """ + ''' pass def convert_colors(self): - """ + ''' Requires: nothing Returns: @@ -238,7 +238,7 @@ class Colors: and print out the tags. If the state if after the color table, look for lines with color info, and substitute the number with the hex number. - """ + ''' self.__initiate_values() with open_for_read(self.__file) as read_obj: with open_for_write(self.__write_to) as self.__write_obj: @@ -255,6 +255,6 @@ class Colors: action(line) copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "color.data") + copy_obj.copy_file(self.__write_to, 'color.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/combine_borders.py b/src/calibre/ebooks/rtf2xml/combine_borders.py index f8ed8d0b05..03a54e96f1 100644 --- a/src/calibre/ebooks/rtf2xml/combine_borders.py +++ b/src/calibre/ebooks/rtf2xml/combine_borders.py @@ -19,7 +19,7 @@ from . import open_for_read, open_for_write class CombineBorders: - """Combine borders in RTF tokens to make later processing easier""" + '''Combine borders in RTF tokens to make later processing easier''' def __init__(self, in_file , @@ -48,7 +48,7 @@ class CombineBorders: return line def end_border(self, line, write_obj): - border_string = "|".join(self.__bord_att) + border_string = '|'.join(self.__bord_att) self.__bord_att = [] write_obj.write('cw<bd<{}<nu<{}\n'.format(self.__bord_pos, border_string)) @@ -88,6 +88,6 @@ class CombineBorders: write_obj.write(self.__default_func(line)) copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "combine_borders.data") + copy_obj.copy_file(self.__write_to, 'combine_borders.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/configure_txt.py b/src/calibre/ebooks/rtf2xml/configure_txt.py index d0414f33a4..db82863eda 100644 --- a/src/calibre/ebooks/rtf2xml/configure_txt.py +++ b/src/calibre/ebooks/rtf2xml/configure_txt.py @@ -12,14 +12,14 @@ class Configure: debug_dir=None, show_config_file=None, ): - """ + ''' Requires: file --file to be read output --file to output to Returns: Nothing. Outputs a file Logic: - """ + ''' self.__configuration_file = configuration_file self.__debug_dir = debug_dir self.__bug_handler = bug_handler diff --git a/src/calibre/ebooks/rtf2xml/convert_to_tags.py b/src/calibre/ebooks/rtf2xml/convert_to_tags.py index 150036c081..749bcb4534 100644 --- a/src/calibre/ebooks/rtf2xml/convert_to_tags.py +++ b/src/calibre/ebooks/rtf2xml/convert_to_tags.py @@ -10,9 +10,9 @@ public_dtd = 'rtf2xml1.0.dtd' class ConvertToTags: - """ + ''' Convert file to XML - """ + ''' def __init__(self, in_file, @@ -49,9 +49,9 @@ class ConvertToTags: self.__bad_encoding = False def __initiate_values(self): - """ + ''' Set values, including those for the dictionary. - """ + ''' self.__state = 'default' self.__new_line = 0 self.__block = ('doc', 'preamble', 'rtf-definition', 'font-table', @@ -79,9 +79,9 @@ class ConvertToTags: } def __open_func(self, line): - """ + ''' Print the opening tag and newlines when needed. - """ + ''' # mi<tg<open______<style-sheet info = line[17:-1] self.__new_line = 0 @@ -92,9 +92,9 @@ class ConvertToTags: self.__write_obj.write('<%s>' % info) def __empty_func(self, line): - """ + ''' Print out empty tag and newlines when needed. - """ + ''' info = line[17:-1] self.__write_obj.write( '<%s/>' % info) @@ -114,7 +114,7 @@ class ConvertToTags: """ # mi<tg<open-att__<footnote<num> info = line[17:-1] - tokens = info.split("<") + tokens = info.split('<') element_name = tokens[0] tokens = tokens[1:] self.__write_obj.write('<%s' % element_name) @@ -145,7 +145,7 @@ class ConvertToTags: """ # mi<tg<open-att__<footnote<num> info = line[17:-1] - tokens = info.split("<") + tokens = info.split('<') element_name = tokens[0] tokens = tokens[1:] self.__write_obj.write('<%s' % element_name) @@ -165,9 +165,9 @@ class ConvertToTags: self.__write_extra_new_line() def __close_func(self, line): - """ + ''' Print out the closed tag and new lines, if appropriate. - """ + ''' # mi<tg<close_____<style-sheet\n info = line[17:-1] self.__write_obj.write( @@ -179,18 +179,18 @@ class ConvertToTags: self.__write_extra_new_line() def __text_func(self, line): - """ + ''' Simply print out the information between [17:-1] - """ + ''' # tx<nu<__________<Normal; # change this! self.__write_obj.write(line[17:-1]) def __write_extra_new_line(self): - """ + ''' Print out extra new lines if the new lines have not exceeded two. If the new lines are greater than two, do nothing. - """ + ''' if not self.__indent: return if self.__new_line < 2: @@ -200,9 +200,9 @@ class ConvertToTags: pass def __write_new_line(self): - """ + ''' Print out a new line if a new line has not already been printed out. - """ + ''' if not self.__indent: return if not self.__new_line: @@ -210,9 +210,9 @@ class ConvertToTags: self.__new_line += 1 def __write_dec(self): - """ + ''' Write the XML declaration at the top of the document. - """ + ''' # keep maximum compatibility with previous version check_encoding_obj = check_encoding.CheckEncoding( bug_handler=self.__bug_handler) @@ -248,7 +248,7 @@ class ConvertToTags: self.__write_new_line() def convert_to_tags(self): - """ + ''' Read in the file one line at a time. Get the important info, between [:16]. Check if this info matches a dictionary entry. If it does, call the appropriate function. @@ -260,7 +260,7 @@ class ConvertToTags: attributes. a closed function for closed tags. an empty tag function. - """ + ''' self.__initiate_values() with open_for_write(self.__write_to) as self.__write_obj: self.__write_dec() @@ -280,6 +280,6 @@ class ConvertToTags: write_obj.write(line) copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "convert_to_tags.data") + copy_obj.copy_file(self.__write_to, 'convert_to_tags.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/copy.py b/src/calibre/ebooks/rtf2xml/copy.py index 7f2fab0898..1d9e9e7331 100644 --- a/src/calibre/ebooks/rtf2xml/copy.py +++ b/src/calibre/ebooks/rtf2xml/copy.py @@ -15,30 +15,30 @@ import shutil class Copy: - """Copy each changed file to a directory for debugging purposes""" - __dir = "" + '''Copy each changed file to a directory for debugging purposes''' + __dir = '' def __init__(self, bug_handler, file=None, deb_dir=None, ): self.__file = file self.__bug_handler = bug_handler def set_dir(self, deb_dir): - """Set the temporary directory to write files to""" + '''Set the temporary directory to write files to''' if deb_dir is None: - message = "No directory has been provided to write to in the copy.py" + message = 'No directory has been provided to write to in the copy.py' raise self.__bug_handler(message) check = os.path.isdir(deb_dir) if not check: - message = "%(deb_dir)s is not a directory" % vars() + message = '%(deb_dir)s is not a directory' % vars() raise self.__bug_handler(message) Copy.__dir = deb_dir def remove_files(self): - """Remove files from directory""" + '''Remove files from directory''' self.__remove_the_files(Copy.__dir) def __remove_the_files(self, the_dir): - """Remove files from directory""" + '''Remove files from directory''' list_of_files = os.listdir(the_dir) for file in list_of_files: rem_file = os.path.join(Copy.__dir,file) @@ -51,11 +51,11 @@ class Copy: pass def copy_file(self, file, new_file): - """ + ''' Copy the file to a new name If the platform is linux, use the faster linux command of cp. Otherwise, use a safe python method. - """ + ''' write_file = os.path.join(Copy.__dir,new_file) shutil.copyfile(file, write_file) diff --git a/src/calibre/ebooks/rtf2xml/default_encoding.py b/src/calibre/ebooks/rtf2xml/default_encoding.py index 6a9a99eb3a..ee7dc31ce8 100644 --- a/src/calibre/ebooks/rtf2xml/default_encoding.py +++ b/src/calibre/ebooks/rtf2xml/default_encoding.py @@ -61,9 +61,9 @@ from . import open_for_read class DefaultEncoding: - """ + ''' Find the default encoding for the doc - """ + ''' # Note: not all those encoding are really supported by rtf2xml # See http://msdn.microsoft.com/en-us/library/windows/desktop/dd317756%28v=vs.85%29.aspx diff --git a/src/calibre/ebooks/rtf2xml/delete_info.py b/src/calibre/ebooks/rtf2xml/delete_info.py index f5743f50d2..c6415932d0 100644 --- a/src/calibre/ebooks/rtf2xml/delete_info.py +++ b/src/calibre/ebooks/rtf2xml/delete_info.py @@ -20,7 +20,7 @@ from . import open_for_read, open_for_write class DeleteInfo: - """Delete unnecessary destination groups""" + '''Delete unnecessary destination groups''' def __init__(self, in_file , @@ -42,9 +42,9 @@ class DeleteInfo: self.__found_delete = False def __initiate_allow(self): - """ + ''' Initiate a list of destination groups which should be printed out. - """ + ''' self.__allowable = ('cw<ss<char-style', 'cw<it<listtable_', 'cw<it<revi-table', @@ -75,8 +75,8 @@ class DeleteInfo: } def __default_func(self,line): - """Handle lines when in no special state. Look for an asterisk to - begin a special state. Otherwise, print out line.""" + '''Handle lines when in no special state. Look for an asterisk to + begin a special state. Otherwise, print out line.''' # cw<ml<asterisk__<nu<true if self.__token_info == 'cw<ml<asterisk__': self.__state = 'after_asterisk' @@ -105,7 +105,7 @@ class DeleteInfo: return False def __asterisk_func(self,line): - """ + ''' Determine whether to delete info in group Note on self.__cb flag. If you find that you are in a delete group, and the previous @@ -114,7 +114,7 @@ class DeleteInfo: destination group. In this case, you have already written the open bracket, so you will need to write the closed one as well. - """ + ''' # Test for {\*}, in which case don't enter # delete state self.__found_delete = True @@ -162,17 +162,17 @@ class DeleteInfo: return False def __found_list_func(self, line): - """ + ''' print out control words in this group - """ + ''' self.__state = 'list' def __list_func(self, line): - """ + ''' Check to see if the group has ended. Return True for all control words. Return False otherwise. - """ + ''' if self.__delete_count == self.__cb_count and \ self.__token_info == 'cb<nu<clos-brack': self.__state = 'default' @@ -186,8 +186,8 @@ class DeleteInfo: return False def delete_info(self): - """Main method for handling other methods. Read one line at - a time, and determine whether to print the line based on the state.""" + '''Main method for handling other methods. Read one line at + a time, and determine whether to print the line based on the state.''' with open_for_read(self.__file) as read_obj: with open_for_write(self.__write_to) as self.__write_obj: for line in read_obj: @@ -207,7 +207,7 @@ class DeleteInfo: self.__write_obj.write(line) copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "delete_info.data") + copy_obj.copy_file(self.__write_to, 'delete_info.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) return self.__found_delete diff --git a/src/calibre/ebooks/rtf2xml/field_strings.py b/src/calibre/ebooks/rtf2xml/field_strings.py index 3e1ec2849f..01533cd4ae 100644 --- a/src/calibre/ebooks/rtf2xml/field_strings.py +++ b/src/calibre/ebooks/rtf2xml/field_strings.py @@ -15,24 +15,24 @@ import sys class FieldStrings: - """ + ''' This module is given a string. It processes the field instruction string and returns a list of three values. - """ + ''' def __init__(self, bug_handler, run_level=1): - """ + ''' Requires: nothing Returns: nothing - """ + ''' self.__run_level = run_level self.__bug_handler = bug_handler self.__initiate_values() def __initiate_values(self): - """ + ''' Requires: nothing. Returns: @@ -41,7 +41,7 @@ class FieldStrings: initiate values for rest of class. self.__field_instruction_dict: The dictionary for all field names. - """ + ''' self.__field_instruction_dict = { # number type (arabic, etc.) and number format (\# " ") 'EDITTIME' : (self.__num_type_and_format_func, 'editing-time'), @@ -226,7 +226,7 @@ class FieldStrings: return the_list def __default_inst_func(self, field_name, name, line): - """ + ''' Requires: field_name -- the first word in the string name -- the changed name according to the dictionary @@ -235,11 +235,11 @@ class FieldStrings: The name of the field. Logic: I only need the changed name for the field. - """ + ''' return [None, None, name] def __fall_back_func(self, field_name, line): - """ + ''' Requires: field_name -- the first word in the string name -- the changed name according to the dictionary @@ -248,13 +248,13 @@ class FieldStrings: The name of the field. Logic: Used for fields not found in dict - """ + ''' the_string = field_name the_string += '<update>none' return [None, None, the_string] def __equation_func(self, field_name, name, line): - """ + ''' Required: field_name -- the first word in the string name --the changed name according to the dictionary @@ -262,11 +262,11 @@ class FieldStrings: Returns: The name of the field Logic: - """ + ''' return [None, None, name] def __no_switch_func(self, field_name, name, line): - """ + ''' Required: field_name --the first field_name -- the first word in the string @@ -275,11 +275,11 @@ class FieldStrings: Returns: The name of the field Logic: - """ + ''' return [None, None, name] def __num_type_and_format_func(self, field_name, name, line): - """ + ''' Required: field_name -- the first word in the string name --the changed name according to the dictionary @@ -289,7 +289,7 @@ class FieldStrings: Logic: parse num_type parse num_format - """ + ''' the_string = name num_format = self.__parse_num_format(line) if num_format: @@ -306,7 +306,7 @@ class FieldStrings: return [None, None, the_string] def __num_format_func(self, field_name, name, line): - """ + ''' Required: field_name -- the first word in the string name --the changed name according to the dictionary @@ -314,7 +314,7 @@ class FieldStrings: Returns: list of None, None, and part of a tag Logic: - """ + ''' the_string = name num_format = self.__parse_num_format(line) if num_format: @@ -322,20 +322,20 @@ class FieldStrings: return [None, None, the_string] def __parse_num_format(self, the_string): - """ + ''' Required: the_string -- the string to parse Returns: a string if the_string contains number formatting information None, otherwise Logic: - """ + ''' match_group = re.search(self.__date_exp, the_string) if match_group: return match_group(1) def __parse_num_type(self, the_string): - """ + ''' Required: the_string -- the string to parse Returns: @@ -347,7 +347,7 @@ class FieldStrings: Get the \\* Upper part. Use a dictionary to convert the "Arabic" to a more-readable word for the value of the key "number-type". (<field number-type = "Arabic"> - """ + ''' match_group = re.search(self.__num_type_exp, the_string) if match_group: name = match_group.group(1) @@ -360,7 +360,7 @@ class FieldStrings: sys.stderr.write('no dictionary entry for %s\n' % name) def __date_func(self, field_name, name, line): - """ + ''' Required: field_name --the fist field_name -- the first word in the string @@ -369,7 +369,7 @@ class FieldStrings: Returns: list of None, None, and part of a tag Logic: - """ + ''' the_string = name match_group = re.search(self.__date_exp, line) if match_group: @@ -377,7 +377,7 @@ class FieldStrings: return [None, None, the_string] def __simple_info_func(self, field_name, name, line): - """ + ''' Required: field_name -- the first word in the string name --the changed name according to the dictionary @@ -390,7 +390,7 @@ class FieldStrings: 2. Lower 3. FirstCap 4. Caps - """ + ''' the_string = name match_group = re.search(self.__format_text_exp, line) if match_group: @@ -405,20 +405,20 @@ class FieldStrings: return [None, None, the_string] def __hyperlink_func(self, field_name, name, line): - """ + ''' Required: field_name -- the first word in the string name --the changed name according to the dictionary line -- the string to be parse Returns: The name of the field - """ + ''' self.__link_switch = re.compile(r'\\l\s{1,}"{0,1}(.*?)"{0,1}\s') the_string = name match_group = re.search(self.__link_switch, line) if match_group: link = match_group.group(1) - link = link.replace('"', """) + link = link.replace('"', '"') the_string += '<link>%s' % link # \l "txt" "link" # want "file name" so must get rid of \c "txt" @@ -441,7 +441,7 @@ class FieldStrings: return [None, None, the_string] def __include_text_func(self, field_name, name, line): - """ + ''' Required: field_name -- the first word in the string name --the changed name according to the dictionary @@ -449,7 +449,7 @@ class FieldStrings: Returns: The name of the field Logic: - """ + ''' the_string = name match_group = re.search(self.__format_text_exp, line) if match_group: @@ -471,7 +471,7 @@ class FieldStrings: match_group = re.search(self.__quote_exp, line) if match_group: arg = match_group.group(1) - arg = arg.replace('"', """) + arg = arg.replace('"', '"') the_string += '<argument>%s' % arg else: sys.stderr.write('Module is field_strings\n') @@ -483,7 +483,7 @@ class FieldStrings: return [None, None, the_string] def __include_pict_func(self, field_name, name, line): - """ + ''' Required: field_name -- the first word in the string name --the changed name according to the dictionary @@ -491,12 +491,12 @@ class FieldStrings: Returns: The name of the field Logic: - """ + ''' the_string = name match_group = re.search(self.__filter_switch, line) if match_group: arg = match_group.group(1) - arg = arg.replace('"', """) + arg = arg.replace('"', '"') the_string += '<filter>%s' % arg # \c "txt" "file name" # want "file name" so must get rid of \c "txt" @@ -515,7 +515,7 @@ class FieldStrings: return [None, None, the_string] def __ref_func(self, field_name, name, line): - """ + ''' Requires: field_name -- the first word in the string name -- the changed name according to the dictionary @@ -527,7 +527,7 @@ class FieldStrings: PAGEREF _Toc440880424 \\h I want to extract the second line of info, which is used as an anchor in the resulting XML file. - """ + ''' the_string = name match_group = re.search(self.__format_text_exp, line) if match_group: @@ -569,7 +569,7 @@ class FieldStrings: return [None, None, the_string] def __toc_table_func(self, field_name, name, line): - """ + ''' Requires: field_name -- the name of the first word in the string name --the changed name, according to the dictionary. @@ -579,7 +579,7 @@ class FieldStrings: Logic: If the string contains Figure, it is a table of figures. Otherwise, it is a plain old table of contents. - """ + ''' the_string = name index = line.find('\\c "Figure"') if index > -1: @@ -588,7 +588,7 @@ class FieldStrings: return [name, None, the_string] def __sequence_func(self, field_name, name, line): - """ + ''' Requires: field_name --the name of the first word in the string. name --the changed name according to the dictionary. @@ -600,14 +600,14 @@ class FieldStrings: whatever--is represented by the second word in the string. Extract and return. SEQ Figure \\* ARABIC - """ + ''' fields = line.split() label = fields[1] my_string = f'{name}<label>{label}' return [None, None, my_string] def __ta_func(self, field_name, name, line): - """ + ''' Requires: field_name --the name of the first word in the string. name --the changed name according to the dictionary. @@ -615,7 +615,7 @@ class FieldStrings: Returns: A string with a value for the type and label attributes Logic: - """ + ''' the_string = name match_group = re.search(self.__ta_short_field_exp, line) if match_group: @@ -638,7 +638,7 @@ class FieldStrings: return [None, None, the_string] def __index_func(self, field_name, name, line): - """ + ''' Requires: field_name --the name of the first word in the string. name --the changed name according to the dictionary. @@ -646,7 +646,7 @@ class FieldStrings: Returns: A string with a value for the type and label attributes Logic: - """ + ''' # self.__index_insert_blank_line_exp = re.compile(r'\\h\s{1,}""') # self.__index_insert_letter_exp = re.compile(r'\\h\s{1,}(".*?")') the_string = name @@ -711,7 +711,7 @@ class FieldStrings: return [None, None, the_string] def __page_ref_func(self, field_name, name, line): - """ + ''' Requires: field_name --first name in the string. name -- the changed name according to the dictionary. @@ -719,7 +719,7 @@ class FieldStrings: Returns: A string . Logic: - """ + ''' the_string = name num_format = self.__parse_num_format(line) if num_format: @@ -742,7 +742,7 @@ class FieldStrings: return [None, None, the_string] def __note_ref_func(self, field_name, name, line): - """ + ''' Requires: field_name --first name in the string. name -- the changed name according to the dictionary. @@ -750,7 +750,7 @@ class FieldStrings: Returns: A string . Logic: - """ + ''' the_string = name line = re.sub(self.__merge_format_exp, '', line) words = line.split() @@ -812,5 +812,5 @@ class FieldStrings: font_size = int(font_size) font_size = '%.2f' % font_size changed_line += 'cw<ci<font-size_<nu<%s\n' % font_size - changed_line += 'tx<hx<__________<\'%s\n' % num + changed_line += "tx<hx<__________<'%s\n" % num return ['Symbol', None, changed_line] diff --git a/src/calibre/ebooks/rtf2xml/fields_large.py b/src/calibre/ebooks/rtf2xml/fields_large.py index 5a208d2eae..9356ebcbc6 100644 --- a/src/calibre/ebooks/rtf2xml/fields_large.py +++ b/src/calibre/ebooks/rtf2xml/fields_large.py @@ -117,9 +117,9 @@ Examples self.__write_to = better_mktemp() def __initiate_values(self): - """ + ''' Initiate all values. - """ + ''' self.__text_string = '' self.__field_instruction_string = '' self.__marker = 'mi<mk<inline-fld\n' @@ -151,7 +151,7 @@ Examples self.__field_string = [] # list of field strings def __before_body_func(self, line): - """ + ''' Required: line --line ro parse Returns: @@ -159,27 +159,27 @@ Examples Logic: Check for the beginninf of the body. If found, changed the state. Always write out the line. - """ + ''' if self.__token_info == 'mi<mk<body-open_': self.__state = 'in_body' self.__write_obj.write(line) def __in_body_func(self, line): - """ + ''' Required: line --line to parse Returns: nothing. (Writes a line to the output file, or performs other actions.) Logic: Check of the beginning of a field. Always output the line. - """ + ''' action = self.__in_body_dict.get(self.__token_info) if action: action(line) self.__write_obj.write(line) def __found_field_func(self, line): - """ + ''' Requires: line --line to parse Returns: @@ -187,7 +187,7 @@ Examples Logic: Set the values for parsing the field. Four lists have to have items appended to them. - """ + ''' self.__state = 'field' self.__cb_count = 0 ob_count = self.__ob_count @@ -197,7 +197,7 @@ Examples self.__par_in_field.append(0) def __in_field_func(self, line): - """ + ''' Requires: line --line to parse Returns: @@ -206,7 +206,7 @@ Examples Check for the end of the field; a paragraph break; a section break; the beginning of another field; or the beginning of the field instruction. - """ + ''' if self.__cb_count == self.__field_count[-1]: self.__field_string[-1] += line self.__end_field_func() @@ -218,7 +218,7 @@ Examples self.__field_string[-1] += line def __par_in_field_func(self, line): - """ + ''' Requires: line --line to parse Returns: @@ -226,12 +226,12 @@ Examples Logic: Write the line to the output file and set the last item in the paragraph in field list to true. - """ + ''' self.__field_string[-1] += line self.__par_in_field[-1] = 1 def __sec_in_field_func(self, line): - """ + ''' Requires: line --line to parse Returns: @@ -239,7 +239,7 @@ Examples Logic: Write the line to the output file and set the last item in the section in field list to true. - """ + ''' self.__field_string[-1] += line self.__sec_in_field[-1] = 1 @@ -284,7 +284,7 @@ Examples self.__field_instruction_string += line def __end_field_func(self): - """ + ''' Requires: nothing Returns: @@ -301,7 +301,7 @@ Examples If the filed list contains more strings, add the latest (processed) string to the last string in the list. Otherwise, write the string to the output file. - """ + ''' last_bracket = self.__field_count.pop() instruction = self.__field_instruction.pop() inner_field_string = self.__field_string.pop() @@ -343,7 +343,7 @@ Examples self.__write_obj.write(the_string) def fix_fields(self): - """ + ''' Requires: nothing Returns: @@ -353,7 +353,7 @@ Examples the state. If the state is before the body, look for the beginning of the body. If the state is body, send the line to the body method. - """ + ''' self.__initiate_values() read_obj = open_for_read(self.__file) self.__write_obj = open_for_write(self.__write_to) @@ -375,6 +375,6 @@ Examples self.__write_obj.close() copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "fields_large.data") + copy_obj.copy_file(self.__write_to, 'fields_large.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/fields_small.py b/src/calibre/ebooks/rtf2xml/fields_small.py index d2a0fbd846..392de17bd8 100644 --- a/src/calibre/ebooks/rtf2xml/fields_small.py +++ b/src/calibre/ebooks/rtf2xml/fields_small.py @@ -61,9 +61,9 @@ file. self.__run_level = run_level def __initiate_values(self): - """ + ''' Initiate all values. - """ + ''' self.__string_obj = field_strings.FieldStrings(bug_handler=self.__bug_handler) self.__state = 'before_body' self.__text_string = '' @@ -88,7 +88,7 @@ file. self.__book_start = re.compile(r'%s' % reg_st) def __before_body_func(self, line): - """ + ''' Requires: line --the line to parse Returns: @@ -96,13 +96,13 @@ file. Logic: Look for the beginning of the body. When found, change the state to body. Always print out the line. - """ + ''' if self.__token_info == 'mi<mk<body-open_': self.__state = 'body' self.__write_obj.write(line) def __body_func(self, line): - """ + ''' Requires: line --the line to parse Returns: @@ -110,7 +110,7 @@ file. Logic: This function handles all the lines in the body of the documents. Look for a bookmark, index or toc entry and take the appropriate action. - """ + ''' action, tag = \ self.__body_dict.get(self.__token_info, (None, None)) if action: @@ -135,7 +135,7 @@ file. self.__type_of_bookmark = tag def __bookmark_func(self, line): - """ + ''' Requires: line --the line to parse Returns: @@ -145,15 +145,15 @@ file. line to a string until the end of the bookmark is found. It processes the string with the fields_string module, and prints out the result. - """ + ''' if self.__beg_bracket_count == self.__cb_count: self.__state = 'body' type = 'bookmark-%s' % self.__type_of_bookmark # change here - """ + ''' my_string = self.__string_obj.process_string( self.__text_string, type) - """ + ''' my_string = self.__parse_bookmark_func( self.__text_string, type) self.__write_obj.write(self.__marker) @@ -239,13 +239,13 @@ file. return changed_string, see_string def __index_bookmark_func(self, my_string): - """ + ''' Requires: my_string -- string in all the index Returns: bookmark_string -- the text string of the book mark index_string -- string minus the bookmark_string - """ + ''' # cw<an<place_____<nu<true in_bookmark = 0 bracket_count = 0 @@ -288,13 +288,13 @@ file. return italics, bold def __parse_toc_func(self, my_string): - """ + ''' Requires: my_string -- all the string in the toc Returns: modidified string Logic: - """ + ''' toc_level = 0 toc_suppress = 0 my_string, book_start_string, book_end_string =\ @@ -324,14 +324,14 @@ file. return my_changed_string def __parse_bookmark_for_toc(self, my_string): - """ + ''' Requires: the_string --string of toc, with new lines Returns: the_string -- string minus bookmarks bookmark_string -- bookmarks Logic: - """ + ''' in_bookmark = 0 bracket_count = 0 book_start_string = '' @@ -370,7 +370,7 @@ file. return toc_string, book_start_string, book_end_string def __parse_bookmark_func(self, my_string, type): - """ + ''' Requires: my_string --string to parse type --type of string @@ -379,7 +379,7 @@ file. Logic: The type is the name (either bookmark-end or bookmark-start). The id is the complete text string. - """ + ''' my_changed_string = ('mi<tg<empty-att_<field<type>%s' '<number>%s<update>none\n' % (type, my_string)) return my_changed_string @@ -401,7 +401,7 @@ file. self.__tag = tag def __toc_index_func(self, line): - """ + ''' Requires: line --the line to parse Returns: @@ -411,7 +411,7 @@ file. adds each line to a string until the end of the entry is found. It processes the string with the fields_string module, and prints out the result. - """ + ''' if self.__beg_bracket_count == self.__cb_count: self.__state = 'body' type = self.__tag @@ -429,7 +429,7 @@ file. self.__text_string += line def fix_fields(self): - """ + ''' Requires: nothing Returns: @@ -440,7 +440,7 @@ file. beginning of the body. The other two states are toc_index (for toc and index entries) and bookmark. - """ + ''' self.__initiate_values() with open_for_read(self.__file) as read_obj: with open_for_write(self.__write_to) as self.__write_obj: @@ -457,6 +457,6 @@ file. action(line) copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "fields_small.data") + copy_obj.copy_file(self.__write_to, 'fields_small.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/fonts.py b/src/calibre/ebooks/rtf2xml/fonts.py index a3fd4b9d53..8cca144fe2 100644 --- a/src/calibre/ebooks/rtf2xml/fonts.py +++ b/src/calibre/ebooks/rtf2xml/fonts.py @@ -20,9 +20,9 @@ from . import open_for_read, open_for_write class Fonts: - """ + ''' Change lines with font info from font numbers to the actual font names. - """ + ''' def __init__(self, in_file, @@ -50,9 +50,9 @@ class Fonts: self.__run_level = run_level def __initiate_values(self): - """ + ''' Initiate all values. - """ + ''' self.__special_font_dict = { 'Symbol' : 0, 'Wingdings' : 0, @@ -73,14 +73,14 @@ class Fonts: self.__wrote_ind_font = 0 def __default_func(self, line): - """ + ''' Requires: line Returns: nothing Handle all lines before the font table. Check for the beginning of the font table. If found, change the state. Print out all lines. - """ + ''' if self.__token_info == 'mi<mk<fonttb-beg': self.__state = 'font_table' self.__write_obj.write(line) @@ -109,7 +109,7 @@ class Fonts: # self.__write_obj.write(line) def __font_in_table_func(self, line): - """ + ''' Requires: line Returns: @@ -125,7 +125,7 @@ class Fonts: dictionary. Also create an empty tag with the name and number as attributes. Preamture end of font table - """ + ''' # cw<ci<font-style<nu<4 # tx<nu<__________<Times; if self.__token_info == 'mi<mk<fontit-end': @@ -147,21 +147,21 @@ class Fonts: self.__state = 'after_font_table' def __found_end_font_table_func(self): - """ + ''' Required: nothing Returns: nothing Logic: If not individual fonts have been written, write one out - """ + ''' if not self.__wrote_ind_font: self.__write_obj.write( 'mi<tg<empty-att_' '<font-in-table<name>Times<num>0\n') def __after_font_table_func(self, line): - """ + ''' Required: line Returns: @@ -174,7 +174,7 @@ class Fonts: the name rather than the number. If the line does not contain font info, simply print it out to the file. - """ + ''' if self.__token_info == 'cw<ci<font-style': font_num = line[20:-1] font_name = self.__font_table.get(font_num) @@ -193,7 +193,7 @@ class Fonts: self.__write_obj.write(line) def convert_fonts(self): - """ + ''' Required: nothing Returns: @@ -205,7 +205,7 @@ class Fonts: tag for each individual font in the font table. If the state is after the font table, look for lines with font info. Substitute a font name for a font number. - """ + ''' self.__initiate_values() with open_for_read(self.__file) as read_obj: with open_for_write(self.__write_to) as self.__write_obj: @@ -221,7 +221,7 @@ class Fonts: self.__special_font_dict['default-font'] = default_font_name copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "fonts.data") + copy_obj.copy_file(self.__write_to, 'fonts.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) return self.__special_font_dict diff --git a/src/calibre/ebooks/rtf2xml/footnote.py b/src/calibre/ebooks/rtf2xml/footnote.py index 7303f95011..96db40c6f3 100644 --- a/src/calibre/ebooks/rtf2xml/footnote.py +++ b/src/calibre/ebooks/rtf2xml/footnote.py @@ -19,12 +19,12 @@ from . import open_for_read, open_for_write class Footnote: - """ + ''' Two public methods are available. The first separates all of the footnotes from the body and puts them at the bottom of the text, where they are easier to process. The second joins those footnotes to the proper places in the body. - """ + ''' def __init__(self, in_file , @@ -39,10 +39,10 @@ class Footnote: self.__found_a_footnote = 0 def __first_line_func(self, line): - """ + ''' Print the tag info for footnotes. Check whether footnote is an endnote and make the tag according to that. - """ + ''' if self.__token_info == 'cw<nt<type______': self.__write_to_foot_obj.write( 'mi<tg<open-att__<footnote<type>endnote<num>%s\n' % self.__footnote_count) @@ -52,7 +52,7 @@ class Footnote: self.__first_line = 0 def __in_footnote_func(self, line): - """Handle all tokens that are part of footnote""" + '''Handle all tokens that are part of footnote''' if self.__first_line: self.__first_line_func(line) if self.__token_info == 'cw<ci<footnot-mk': @@ -74,7 +74,7 @@ class Footnote: self.__write_to_foot_obj.write(line) def __found_footnote(self, line): - """ Found a footnote""" + ''' Found a footnote''' self.__found_a_footnote = 1 self.__in_footnote = 1 self.__first_line = 1 @@ -88,7 +88,7 @@ class Footnote: 'mi<mk<footnt-ope<%04d\n' % self.__footnote_count) def __default_sep(self, line): - """Handle all tokens that are not footnote tokens""" + '''Handle all tokens that are not footnote tokens''' if self.__token_info == 'cw<nt<footnote__': self.__found_footnote(line) self.__write_obj.write(line) @@ -99,9 +99,9 @@ class Footnote: ) def __initiate_sep_values(self): - """ + ''' initiate counters for separate_footnotes method. - """ + ''' self.__bracket_count=0 self.__ob_count = 0 self.__cb_count = 0 @@ -111,13 +111,13 @@ class Footnote: self.__footnote_count = 0 def separate_footnotes(self): - """ + ''' Separate all the footnotes in an RTF file and put them at the bottom, where they are easier to process. Each time a footnote is found, print all of its contents to a temporary file. Close both the main and temporary file. Print the footnotes from the temporary file to the bottom of the main file. - """ + ''' self.__initiate_sep_values() self.__footnote_holder = better_mktemp() with open_for_read(self.__file) as read_obj: @@ -152,21 +152,21 @@ class Footnote: os.remove(self.__footnote_holder) copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "footnote_separate.data") + copy_obj.copy_file(self.__write_to, 'footnote_separate.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) def update_info(self, file, copy): - """ + ''' Unused method - """ + ''' self.__file = file self.__copy = copy def __get_foot_body_func(self, line): - """ + ''' Process lines in main body and look for beginning of footnotes. - """ + ''' # mi<mk<footnt-end if self.__token_info == 'mi<mk<footnt-beg': self.__state = 'foot' @@ -174,9 +174,9 @@ class Footnote: self.__write_obj.write(line) def __get_foot_foot_func(self, line): - """ + ''' Copy footnotes from bottom of file to a separate, temporary file. - """ + ''' if self.__token_info == 'mi<mk<footnt-end': self.__state = 'body' else: @@ -201,12 +201,12 @@ class Footnote: self.__get_foot_foot_func(line) def __get_foot_from_temp(self, num): - """ + ''' Private method for joining footnotes to body. This method reads from the temporary file until the proper footnote marker is found. It collects all the tokens until the end of the footnote, and returns them as a string. - """ + ''' look_for = 'mi<mk<footnt-ope<' + num + '\n' found_foot = 0 string_to_return = '' @@ -220,14 +220,14 @@ class Footnote: found_foot = 1 def __join_from_temp(self): - """ + ''' Private method for rejoining footnotes to body. Read from the newly-created, temporary file that contains the body text but no footnotes. Each time a footnote marker is found, call the private method __get_foot_from_temp(). This method will return a string to print out to the third file. If no footnote marker is found, simply print out the token (line). - """ + ''' with open_for_read(self.__footnote_holder) as self.__read_from_foot_obj: with open_for_read(self.__write_to) as read_obj: with open_for_write(self.__write_to2) as self.__write_obj: @@ -237,7 +237,7 @@ class Footnote: self.__write_obj.write(line) def join_footnotes(self): - """ + ''' Join the footnotes from the bottom of the file and put them in their former places. First, remove the footnotes from the bottom of the input file, outputting them to a temporary file. This creates two new @@ -245,7 +245,7 @@ class Footnote: these files to read. When a marker is found in the main file, find the corresponding marker in the footnote file. Output the mix of body and footnotes to a third file. - """ + ''' if not self.__found_a_footnote: return self.__write_to2 = better_mktemp() @@ -256,7 +256,7 @@ class Footnote: # self.__read_from_foot_obj.close() copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to2, "footnote_joined.data") + copy_obj.copy_file(self.__write_to2, 'footnote_joined.data') copy_obj.rename(self.__write_to2, self.__file) os.remove(self.__write_to2) os.remove(self.__footnote_holder) diff --git a/src/calibre/ebooks/rtf2xml/get_char_map.py b/src/calibre/ebooks/rtf2xml/get_char_map.py index 4ce42e37c8..62cd7b43c6 100644 --- a/src/calibre/ebooks/rtf2xml/get_char_map.py +++ b/src/calibre/ebooks/rtf2xml/get_char_map.py @@ -13,11 +13,11 @@ class GetCharMap: - """ + ''' Return the character map for the given value - """ + ''' def __init__(self, bug_handler, char_file): """ diff --git a/src/calibre/ebooks/rtf2xml/get_options.py b/src/calibre/ebooks/rtf2xml/get_options.py index 5c5fd60b2f..f3d85acbd8 100644 --- a/src/calibre/ebooks/rtf2xml/get_options.py +++ b/src/calibre/ebooks/rtf2xml/get_options.py @@ -10,9 +10,9 @@ # # # # ######################################################################### -""" +''' Gets options for main part of script -""" +''' import os import sys @@ -33,9 +33,9 @@ class GetOptions: self.__bug_handler = bug_handler def get_options(self): - """ + ''' return valid, output, help, show_warnings, debug, file - """ + ''' return_options = self.__get_config_options() options_dict = { 'dir' : [1], @@ -107,11 +107,11 @@ class GetOptions: return_options['out-file'] = options['output'] else: pass - """ + ''' sys.stderr.write( 'You must provide an output file with the \'o\' option\n') return_options['valid'] = 0 - """ + ''' if 'level' in the_keys: return_options['level'] = options['level'] the_level = return_options.get('level') @@ -129,8 +129,8 @@ class GetOptions: if 'format' in the_keys: format = options['format'] if format not in acceptable: - sys.stderr.write('--format must take either \'sdoc\' or ' - '\'tei\'\n') + sys.stderr.write("--format must take either 'sdoc' or " + "'tei'\n") return_options['valid'] = 0 return return_options else: @@ -225,14 +225,14 @@ class GetOptions: else: return_options['out-file'] = '%s.xml' % the_file_name if not smart_output and not return_options['out-file']: - """ + ''' sys.stderr.write( 'Please provide and file to output with the -o option.\n' 'Or set \'<smart-output value = "true"/>\'.\n' 'in the configuration file.\n' ) return_options['valid'] = 0 - """ + ''' pass if 'indent' in the_keys: try: @@ -242,7 +242,7 @@ class GetOptions: sys.stderr.write('--indent must take an integer') return_options['valid'] = 0 # check for format and pyxml - """ + ''' the_format = return_options.get('format') if the_format != 'raw': no_pyxml = return_options.get('no-pyxml') @@ -263,7 +263,7 @@ class GetOptions: % the_format ) return_options['valid'] = 0 - """ + ''' return return_options def __get_config_options(self): diff --git a/src/calibre/ebooks/rtf2xml/group_borders.py b/src/calibre/ebooks/rtf2xml/group_borders.py index 3b6f118393..b072a6361f 100644 --- a/src/calibre/ebooks/rtf2xml/group_borders.py +++ b/src/calibre/ebooks/rtf2xml/group_borders.py @@ -53,7 +53,7 @@ class GroupBorders: self.__wrap = wrap def __initiate_values(self): - """ + ''' Required: Nothing Return: @@ -61,12 +61,12 @@ class GroupBorders: Logic: The self.__end_list is a list of tokens that will force a list to end. Likewise, the self.__end_lines is a list of lines that forces a list to end. - """ - self.__state = "default" + ''' + self.__state = 'default' self.__left_indent = 0 self.__border_num = 0 self.__list_type = 'not-defined' - self.__pard_def = "" + self.__pard_def = '' self.__all_lists = [] self.__list_chunk = '' self.__state_dict={ @@ -139,13 +139,13 @@ class GroupBorders: self.__write_obj.write(line) def __after_pard_func(self, line): - """ + ''' Required: line -- the line of current text. Return: Nothing Logic: - """ + ''' if self.__token_info == 'mi<tg<open-att__' \ and line[17:37] == 'paragraph-definition': # found paragraph definition @@ -174,14 +174,14 @@ class GroupBorders: self.__state = 'default' def __pard_after_par_def_func(self, line): - """ + ''' Required: line -- the line of current text. id -- the id of the current list Return: Nothing Logic: - """ + ''' is_border = self.__is_border_func(line) if not is_border: self.__write_obj.write('mi<tg<close_____<paragraph-definition\n') @@ -211,7 +211,7 @@ class GroupBorders: self.__list_chunk = '' def __default_func(self, line): - """ + ''' Required: self, line Returns: @@ -220,7 +220,7 @@ class GroupBorders: Look for the start of a paragraph definition. If one is found, check if it contains a list-id. If it does, start a list. Change the state to in_pard. - """ + ''' if self.__token_info == 'mi<tg<open-att__' \ and line[17:37] == 'paragraph-definition': contains_border = self.__is_border_func(line) @@ -282,13 +282,13 @@ class GroupBorders: self.__style_name = line[17:-1] def group_borders(self): - """ + ''' Required: nothing Returns: original file will be changed Logic: - """ + ''' self.__initiate_values() read_obj = open_for_read(self.__file) self.__write_obj = open_for_write(self.__write_to) @@ -304,6 +304,6 @@ class GroupBorders: self.__write_obj.close() copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "group_borders.data") + copy_obj.copy_file(self.__write_to, 'group_borders.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/group_styles.py b/src/calibre/ebooks/rtf2xml/group_styles.py index fa8bbaec44..38b6966b7e 100644 --- a/src/calibre/ebooks/rtf2xml/group_styles.py +++ b/src/calibre/ebooks/rtf2xml/group_styles.py @@ -53,7 +53,7 @@ class GroupStyles: self.__wrap = wrap def __initiate_values(self): - """ + ''' Required: Nothing Return: @@ -61,11 +61,11 @@ class GroupStyles: Logic: The self.__end_list is a list of tokens that will force a list to end. Likewise, the self.__end_lines is a list of lines that forces a list to end. - """ - self.__state = "default" + ''' + self.__state = 'default' self.__left_indent = 0 self.__list_type = 'not-defined' - self.__pard_def = "" + self.__pard_def = '' self.__all_lists = [] self.__list_chunk = '' self.__state_dict={ @@ -128,13 +128,13 @@ class GroupStyles: self.__write_obj.write(line) def __after_pard_func(self, line): - """ + ''' Required: line -- the line of current text. Return: Nothing Logic: - """ + ''' if self.__token_info == 'mi<tg<open-att__' \ and line[17:37] == 'paragraph-definition': # found paragraph definition @@ -175,14 +175,14 @@ class GroupStyles: self.__write_obj.write('mi<mk<stylegend_\n') def __pard_after_par_def_func(self, line): - """ + ''' Required: line -- the line of current text. id -- the id of the current list Return: Nothing Logic: - """ + ''' if self.__last_style_name == self.__style_name: # just keep going if self.__wrap: @@ -204,7 +204,7 @@ class GroupStyles: self.__list_chunk = '' def __default_func(self, line): - """ + ''' Required: self, line Returns: @@ -213,7 +213,7 @@ class GroupStyles: Look for the start of a paragraph definition. If one is found, check if it contains a list-id. If it does, start a list. Change the state to in_pard. - """ + ''' if self.__token_info == 'mi<tg<open-att__' \ and line[17:37] == 'paragraph-definition': self.__state = 'in_pard' @@ -228,13 +228,13 @@ class GroupStyles: self.__style_name = line[17:-1] def group_styles(self): - """ + ''' Required: nothing Returns: original file will be changed Logic: - """ + ''' self.__initiate_values() read_obj = open_for_read(self.__file) self.__write_obj = open_for_write(self.__write_to) @@ -250,6 +250,6 @@ class GroupStyles: self.__write_obj.close() copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "group_styles.data") + copy_obj.copy_file(self.__write_to, 'group_styles.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/header.py b/src/calibre/ebooks/rtf2xml/header.py index 85d8a384ed..b10ee961b1 100644 --- a/src/calibre/ebooks/rtf2xml/header.py +++ b/src/calibre/ebooks/rtf2xml/header.py @@ -20,12 +20,12 @@ from . import open_for_read, open_for_write class Header: - """ + ''' Two public methods are available. The first separates all of the headers and footers from the body and puts them at the bottom of the text, where they are easier to process. The second joins those headers and footers to the proper places in the body. - """ + ''' def __init__(self, in_file , @@ -40,9 +40,9 @@ class Header: self.__found_a_header = False def __in_header_func(self, line): - """ + ''' Handle all tokens that are part of header - """ + ''' if self.__cb_count == self.__header_bracket_count: self.__in_header = False self.__write_obj.write(line) @@ -54,9 +54,9 @@ class Header: self.__write_to_head_obj.write(line) def __found_header(self, line): - """ + ''' Found a header - """ + ''' # but this could be header or footer self.__found_a_header = True self.__in_header = True @@ -85,17 +85,17 @@ class Header: ) def __default_sep(self, line): - """ + ''' Handle all tokens that are not header tokens - """ + ''' if self.__token_info[3:5] == 'hf': self.__found_header(line) self.__write_obj.write(line) def __initiate_sep_values(self): - """ + ''' initiate counters for separate_footnotes method. - """ + ''' self.__bracket_count=0 self.__ob_count = 0 self.__cb_count = 0 @@ -114,13 +114,13 @@ class Header: } def separate_headers(self): - """ + ''' Separate all the footnotes in an RTF file and put them at the bottom, where they are easier to process. Each time a footnote is found, print all of its contents to a temporary file. Close both the main and temporary file. Print the footnotes from the temporary file to the bottom of the main file. - """ + ''' self.__initiate_sep_values() self.__header_holder = better_mktemp() with open_for_read(self.__file) as read_obj: @@ -152,21 +152,21 @@ class Header: copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "header_separate.data") + copy_obj.copy_file(self.__write_to, 'header_separate.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) def update_info(self, file, copy): - """ + ''' Unused method - """ + ''' self.__file = file self.__copy = copy def __get_head_body_func(self, line): - """ + ''' Process lines in main body and look for beginning of headers. - """ + ''' # mi<mk<footnt-end if self.__token_info == 'mi<mk<header-beg': self.__state = 'head' @@ -174,9 +174,9 @@ class Header: self.__write_obj.write(line) def __get_head_head_func(self, line): - """ + ''' Copy headers and footers from bottom of file to a separate, temporary file. - """ + ''' if self.__token_info == 'mi<mk<header-end': self.__state = 'body' else: @@ -201,12 +201,12 @@ class Header: self.__get_head_head_func(line) def __get_head_from_temp(self, num): - """ + ''' Private method for joining headers and footers to body. This method reads from the temporary file until the proper footnote marker is found. It collects all the tokens until the end of the footnote, and returns them as a string. - """ + ''' look_for = 'mi<mk<header-ope<' + num + '\n' found_head = False string_to_return = '' @@ -220,14 +220,14 @@ class Header: found_head = True def __join_from_temp(self): - """ + ''' Private method for rejoining footnotes to body. Read from the newly-created, temporary file that contains the body text but no footnotes. Each time a footnote marker is found, call the private method __get_foot_from_temp(). This method will return a string to print out to the third file. If no footnote marker is found, simply print out the token (line). - """ + ''' self.__read_from_head_obj = open_for_read(self.__header_holder) self.__write_obj = open_for_write(self.__write_to2) with open_for_read(self.__write_to) as read_obj: @@ -237,7 +237,7 @@ class Header: self.__write_obj.write(line) def join_headers(self): - """ + ''' Join the footnotes from the bottom of the file and put them in their former places. First, remove the footnotes from the bottom of the input file, outputting them to a temporary file. This creates two new @@ -245,7 +245,7 @@ class Header: these files to read. When a marker is found in the main file, find the corresponding marker in the footnote file. Output the mix of body and footnotes to a third file. - """ + ''' if not self.__found_a_header: return self.__write_to2 = better_mktemp() @@ -256,7 +256,7 @@ class Header: self.__read_from_head_obj.close() copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "header_join.data") + copy_obj.copy_file(self.__write_to, 'header_join.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) os.remove(self.__header_holder) diff --git a/src/calibre/ebooks/rtf2xml/headings_to_sections.py b/src/calibre/ebooks/rtf2xml/headings_to_sections.py index 6253f18f33..bc5ba3e5c1 100644 --- a/src/calibre/ebooks/rtf2xml/headings_to_sections.py +++ b/src/calibre/ebooks/rtf2xml/headings_to_sections.py @@ -20,8 +20,8 @@ from . import open_for_read, open_for_write class HeadingsToSections: - """ - """ + ''' + ''' def __init__(self, in_file, @@ -45,7 +45,7 @@ class HeadingsToSections: self.__write_to = better_mktemp() def __initiate_values(self): - """ + ''' Required: Nothing Return: @@ -53,8 +53,8 @@ class HeadingsToSections: Logic: The self.__end_list is a list of tokens that will force a list to end. Likewise, the self.__end_lines is a list of lines that forces a list to end. - """ - self.__state = "default" + ''' + self.__state = 'default' self.__all_sections = [] self.__chunk = '' self.__state_dict={ @@ -88,7 +88,7 @@ class HeadingsToSections: self.__id_regex = re.compile(r'\<list-id\>(\d+)') def __close_lists(self): - """ + ''' Required: Nothing Return: @@ -100,7 +100,7 @@ class HeadingsToSections: Keep track of how many levels you close. Reduce the list by that many levels. Reverse the list again. - """ + ''' current_indent = self.__left_indent self.__all_lists.reverse() num_levels_closed = 0 @@ -145,7 +145,7 @@ class HeadingsToSections: self.__write_obj.write('mi<tg<close_____<section\n') def __default_func(self, line): - """ + ''' Required: self, line Returns: @@ -154,7 +154,7 @@ class HeadingsToSections: Look for the start of a paragraph definition. If one is found, check if it contains a list-id. If it does, start a list. Change the state to in_pard. - """ + ''' if self.__token_info == 'mi<mk<sect-start': self.__section_num[0] += 1 self.__section_num = self.__section_num[0:1] @@ -203,13 +203,13 @@ class HeadingsToSections: self.__write_obj.write(line) def make_sections(self): - """ + ''' Required: nothing Returns: original file will be changed Logic: - """ + ''' self.__initiate_values() read_obj = open_for_read(self.__file) self.__write_obj = open_for_write(self.__write_to) @@ -224,6 +224,6 @@ class HeadingsToSections: self.__write_obj.close() copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "sections_to_headings.data") + copy_obj.copy_file(self.__write_to, 'sections_to_headings.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/hex_2_utf8.py b/src/calibre/ebooks/rtf2xml/hex_2_utf8.py index 86905baeff..c95b9afce5 100644 --- a/src/calibre/ebooks/rtf2xml/hex_2_utf8.py +++ b/src/calibre/ebooks/rtf2xml/hex_2_utf8.py @@ -22,9 +22,9 @@ from . import open_for_read, open_for_write class Hex2Utf8: - """ + ''' Convert Microsoft hexadecimal numbers to utf-8 - """ + ''' def __init__(self, in_file, @@ -139,7 +139,7 @@ class Hex2Utf8: # self.__convert_zapf = 0 def __initiate_values(self): - """ + ''' Required: Nothing Set values, including those for the dictionaries. @@ -147,7 +147,7 @@ class Hex2Utf8: sets. For example, for the Symbol font, there is the standard part for hexadecimal numbers, and the part for Microsoft characters. Read each part in, and then combine them. - """ + ''' # the default encoding system, the lower map for characters 0 through # 128, and the encoding system for Microsoft characters. # New on 2004-05-8: the self.__char_map is not in directory with other @@ -232,7 +232,7 @@ class Hex2Utf8: converted = self.__current_dict.get(hex_num) if converted is not None: # tag as utf-8 - if converted[0:1] == "&": + if converted[0:1] == '&': font = self.__current_dict_name if self.__convert_caps\ and self.__caps_list[-1] == 'true'\ @@ -272,9 +272,9 @@ class Hex2Utf8: self.__write_obj.write(line) def __body_func(self, line): - """ + ''' When parsing preamble - """ + ''' self.__write_obj.write(line) def __preamble_func(self, line): @@ -298,32 +298,32 @@ class Hex2Utf8: action(line) copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "preamble_utf_convert.data") + copy_obj.copy_file(self.__write_to, 'preamble_utf_convert.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) def __preamble_for_body_func(self, line): - """ + ''' Required: line -- line to parse Returns: nothing Logic: Used when parsing the body. - """ + ''' if self.__token_info == 'mi<mk<body-open_': self.__found_body_func(line) self.__write_obj.write(line) def __body_for_body_func(self, line): - """ + ''' Required: line -- line to parse Returns: nothing Logic: Used when parsing the body. - """ + ''' action = self.__in_body_dict.get(self.__token_info) if action is not None: action(line) @@ -331,14 +331,14 @@ class Hex2Utf8: self.__write_obj.write(line) def __start_font_func(self, line): - """ + ''' Required: line -- line to parse Returns: nothing Logic: add font face to font_list - """ + ''' face = line[17:-1] self.__font_list.append(face) if face == 'Symbol' and self.__convert_symbol: @@ -355,14 +355,14 @@ class Hex2Utf8: self.__current_dict = self.__def_dict def __end_font_func(self, line): - """ + ''' Required: line -- line to parse Returns: nothing Logic: pop font_list - """ + ''' if len(self.__font_list) > 1: self.__font_list.pop() else: @@ -384,14 +384,14 @@ class Hex2Utf8: self.__current_dict = self.__def_dict def __start_special_font_func_old(self, line): - """ + ''' Required: line -- line Returns; nothing Logic: change the dictionary to use in conversion - """ + ''' # for error checking if self.__token_info == 'mi<mk<font-symbo': self.__current_dict.append(self.__symbol_dict) @@ -407,18 +407,18 @@ class Hex2Utf8: self.__current_dict_name = 'Zapf Dingbats' def __end_special_font_func(self, line): - """ + ''' Required: line --line to parse Returns: nothing Logic: pop the last dictionary, which should be a special font - """ + ''' if len(self.__current_dict) < 2: sys.stderr.write('module is hex_2_utf 8\n') sys.stderr.write('method is __end_special_font_func\n') - sys.stderr.write('less than two dictionaries --can\'t pop\n') + sys.stderr.write("less than two dictionaries --can't pop\n") self.__special_fonts_found -= 1 else: self.__current_dict.pop() @@ -426,7 +426,7 @@ class Hex2Utf8: self.__dict_name = 'default' def __start_caps_func_old(self, line): - """ + ''' Required: line -- line to parse Returns: @@ -434,11 +434,11 @@ class Hex2Utf8: Logic: A marker that marks the start of caps has been found. Set self.__in_caps to 1 - """ + ''' self.__in_caps = 1 def __start_caps_func(self, line): - """ + ''' Required: line -- line to parse Returns: @@ -446,13 +446,13 @@ class Hex2Utf8: Logic: A marker that marks the start of caps has been found. Set self.__in_caps to 1 - """ + ''' self.__in_caps = 1 value = line[17:-1] self.__caps_list.append(value) def __end_caps_func(self, line): - """ + ''' Required: line -- line to parse Returns: @@ -460,7 +460,7 @@ class Hex2Utf8: Logic: A marker that marks the end of caps has been found. set self.__in_caps to 0 - """ + ''' if len(self.__caps_list) > 1: self.__caps_list.pop() else: @@ -469,14 +469,14 @@ class Hex2Utf8: 'caps list should be more than one?\n') # self.__in_caps not set def __text_func(self, line): - """ + ''' Required: line -- line to parse Returns: nothing Logic: if in caps, convert. Otherwise, print out. - """ + ''' text = line[17:-1] # print line if self.__current_dict_name in ('Symbol', 'Wingdings', 'Zapf Dingbats'): @@ -486,7 +486,7 @@ class Hex2Utf8: hex_num = str(hex_num) hex_num = hex_num.upper() hex_num = hex_num[2:] - hex_num = '\'%s' % hex_num + hex_num = "'%s" % hex_num converted = self.__current_dict.get(hex_num) if converted is None: sys.stderr.write('module is hex_2_ut8\nmethod is __text_func\n') @@ -503,14 +503,14 @@ class Hex2Utf8: self.__write_obj.write('tx<nu<__________<%s\n' % text) def __utf_to_caps_func(self, line): - """ + ''' Required: line -- line to parse returns nothing Logic Get the text, and use another method to convert - """ + ''' utf_text = line[17:-1] if self.__caps_list[-1] == 'true' and self.__convert_caps: # utf_text = utf_text.upper() @@ -518,7 +518,7 @@ class Hex2Utf8: self.__write_obj.write('tx<ut<__________<%s\n' % utf_text) def __utf_token_to_caps_func(self, char_entity): - """ + ''' Required: utf_text -- such as &xxx; Returns: @@ -527,7 +527,7 @@ class Hex2Utf8: RTF often stores text in the improper values. For example, a capital umlaut o (?), is stores as ?. This function swaps the case by looking up the value in a dictionary. - """ + ''' hex_num = char_entity[3:] length = len(hex_num) if length == 3: @@ -556,7 +556,7 @@ class Hex2Utf8: action(line) copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "body_utf_convert.data") + copy_obj.copy_file(self.__write_to, 'body_utf_convert.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) @@ -568,7 +568,7 @@ class Hex2Utf8: self.__convert_body() -""" +''' how to swap case for non-capitals my_string.swapcase() An example of how to use a hash for the caps function @@ -586,4 +586,4 @@ line = "а more text" reg_exp = re.compile(r'(?P<name>а|б)') line2 = re.sub(reg_exp, my_sub_func, line) print line2 -""" +''' diff --git a/src/calibre/ebooks/rtf2xml/info.py b/src/calibre/ebooks/rtf2xml/info.py index 821b3ee68e..ff6f8d8adf 100644 --- a/src/calibre/ebooks/rtf2xml/info.py +++ b/src/calibre/ebooks/rtf2xml/info.py @@ -21,9 +21,9 @@ from . import open_for_read, open_for_write class Info: - """ + ''' Make tags for document-information - """ + ''' def __init__(self, in_file, @@ -48,9 +48,9 @@ class Info: self.__write_to = better_mktemp() def __initiate_values(self): - """ + ''' Initiate all values. - """ + ''' self.__text_string = '' self.__state = 'before_info_table' self.rmspace = re.compile(r'\s+') @@ -108,7 +108,7 @@ class Info: } def __before_info_table_func(self, line): - """ + ''' Required: line -- the line to parse Returns: @@ -116,13 +116,13 @@ class Info: Logic: Check for the beginning of the information table. When found, set the state to the information table. Always write the line. - """ + ''' if self.__token_info == 'mi<mk<doc-in-beg': self.__state = 'in_info_table' self.__write_obj.write(line) def __in_info_table_func(self, line): - """ + ''' Requires: line -- line to parse Returns: @@ -132,7 +132,7 @@ class Info: token has a special value in the info table dictionary. If it does, execute that function. Otherwise, output the line to the file. - """ + ''' if self.__token_info == 'mi<mk<doc-in-end': self.__state = 'after_info_table' else: @@ -143,7 +143,7 @@ class Info: self.__write_obj.write(line) def __found_tag_with_text_func(self, line, tag): - """ + ''' Requires: line -- line to parse tag --what kind of line @@ -153,12 +153,12 @@ class Info: This function marks the beginning of information fields that have text that must be collected. Set the type of information field with the tag option. Set the state to collecting text - """ + ''' self.__tag = tag self.__state = 'collect_text' def __collect_text_func(self, line): - """ + ''' Requires: line -- line to parse Returns: @@ -167,7 +167,7 @@ class Info: If the end of the information field is found, write the text string to the file. Otherwise, if the line contains text, add it to the text string. - """ + ''' if self.__token_info == 'mi<mk<docinf-end': self.__state = 'in_info_table' # Don't print empty tags @@ -182,7 +182,7 @@ class Info: self.__text_string += line[17:-1] def __found_tag_with_tokens_func(self, line, tag): - """ + ''' Requires: line -- line to parse tag -- type of field @@ -193,13 +193,13 @@ class Info: that must be parsed as attributes for the element. Set the state to collect tokesn, and set the text string to start an empty element with attributes. - """ + ''' self.__state = 'collect_tokens' self.__text_string = 'mi<tg<empty-att_<%s' % tag # mi<tg<empty-att_<page-definition<margin>33\n def __collect_tokens_func(self, line): - """ + ''' Requires: line -- line to parse Returns: @@ -217,7 +217,7 @@ class Info: dictionary, print out an error message. Otherwise add the value to the text string. (num-of-wor => number-of-words) - """ + ''' # cw<di<year______<nu<2003 if self.__token_info == 'mi<mk<docinf-end': self.__state = 'in_info_table' @@ -243,7 +243,7 @@ class Info: ) def __after_info_table_func(self, line): - """ + ''' Requires: line --line to write to file Returns: @@ -251,11 +251,11 @@ class Info: Logic: After the end of the information table, simple write the line to the file. - """ + ''' self.__write_obj.write(line) def fix_info(self): - """ + ''' Requires: nothing Returns: @@ -269,7 +269,7 @@ class Info: style table, look for lines with style info, and substitute the number with the name of the style. If the state if after the information table, simply write the line to the output file. - """ + ''' self.__initiate_values() with open_for_read(self.__file) as read_obj: with open_for_write(self.__write_to) as self.__write_obj: @@ -282,6 +282,6 @@ class Info: action(line) copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "info.data") + copy_obj.copy_file(self.__write_to, 'info.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/inline.py b/src/calibre/ebooks/rtf2xml/inline.py index eadb8d2daf..6360c75737 100644 --- a/src/calibre/ebooks/rtf2xml/inline.py +++ b/src/calibre/ebooks/rtf2xml/inline.py @@ -6,7 +6,7 @@ from calibre.ptempfile import better_mktemp from . import open_for_read, open_for_write -""" +''' States. 1. default 1. an open bracket ends this state. @@ -16,14 +16,14 @@ States. 1. The lack of a control word ends this state. 2. paragraph end -- close out all tags 3. footnote beg -- close out all tags -""" +''' class Inline: - """ + ''' Make inline tags within lists. Logic: - """ + ''' def __init__(self, in_file, @@ -47,9 +47,9 @@ class Inline: self.__write_to = better_mktemp() def __initiate_values(self): - """ + ''' Initiate all values. - """ + ''' self.__state_dict = { 'default': self.__default_func, 'after_open_bracket': self.__after_open_bracket_func, @@ -120,13 +120,13 @@ class Inline: self.__caps_list = ['false'] def __set_list_func(self, line): - """ + ''' Requires: line--line of text Returns: nothing Logic: - """ + ''' if self.__place == 'in_list': if self.__token_info == 'mi<mk<lst-tx-end': self.__place = 'not_in_list' @@ -139,14 +139,14 @@ class Inline: self.__groups_in_waiting = self.__groups_in_waiting_list def __default_func(self, line): - """ + ''' Requires: line-- line of text Returns: nothing Logic: Write if not hardline break - """ + ''' action = self.__default_dict.get(self.__token_info) if action: action(line) @@ -168,7 +168,7 @@ class Inline: self.__inline_list[-1]['contains_inline'] = 0 def __after_open_bracket_func(self, line): - """ + ''' Requires: line --line of text Returns: @@ -178,7 +178,7 @@ class Inline: method to add to the dictionary. Use the dictionary to get the appropriate function. Always print out the line. - """ + ''' if line[0:5] == 'cw<ci': # calibre: bug in original function no diff between cw<ci and cw<pf self.__handle_control_word(line) else: @@ -189,7 +189,7 @@ class Inline: self.__write_obj.write(line) def __handle_control_word(self, line): - """ + ''' Required: line --line of text Returns: @@ -200,7 +200,7 @@ class Inline: If the font style of Symbol, Wingdings, or Dingbats is found, always mark this. I need this later to convert the text to the right utf. - """ + ''' # cw<ci<shadow_____<nu<true # self.__char_dict = { char_info = line[6:16] @@ -209,7 +209,7 @@ class Inline: if name: self.__inline_list[-1]['contains_inline'] = 1 self.__inline_list[-1][name] = char_value - """ + ''' if name == 'font-style': if char_value == 'Symbol': self.__write_obj.write('mi<mk<font-symbo\n') @@ -217,7 +217,7 @@ class Inline: self.__write_obj.write('mi<mk<font-wingd\n') elif char_value == 'Zapf Dingbats': self.__write_obj.write('mi<mk<font-dingb\n') - """ + ''' def __close_bracket_func(self, line): """ @@ -259,7 +259,7 @@ class Inline: self.__groups_in_waiting[0] -= 1 def __found_text_func(self, line): - """ + ''' Required: line--line of text Return: @@ -271,7 +271,7 @@ class Inline: Text can mark the start of a paragraph. If already in a paragraph, check to see if any groups are waiting to be added. If so, use another method to write these groups. - """ + ''' if self.__place == 'in_list': self.__write_inline() else: @@ -326,7 +326,7 @@ class Inline: self.__groups_in_waiting[0] = 0 def __end_para_func(self, line): - """ + ''' Requires: line -- line of text Returns: @@ -335,7 +335,7 @@ class Inline: Slice from the end the groups in waiting. Iterate through the list. If the dictionary contaings info, write a closing tag. - """ + ''' if not self.__in_para: return if self.__groups_in_waiting[0] == 0: @@ -355,7 +355,7 @@ class Inline: self.__in_para = 0 def __start_para_func(self, line): - """ + ''' Requires: line -- line of text Returns: @@ -364,7 +364,7 @@ class Inline: Iterate through the self.__inline_list to get each dict. If the dict containst inline info, get the keys. Iterate through the keys and print out the key and value. - """ + ''' for the_dict in self.__inline_list: contains_info = the_dict.get('contains_inline') if contains_info : @@ -390,7 +390,7 @@ class Inline: pass def form_tags(self): - """ + ''' Requires: area--area to parse (list or non-list) Returns: @@ -398,7 +398,7 @@ class Inline: Logic: Read one line in at a time. Determine what action to take based on the state. - """ + ''' self.__initiate_values() with open_for_read(self.__file) as read_obj: with open_for_write(self.__write_to) as self.__write_obj: @@ -423,6 +423,6 @@ class Inline: action(line) copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "inline.data") + copy_obj.copy_file(self.__write_to, 'inline.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/line_endings.py b/src/calibre/ebooks/rtf2xml/line_endings.py index e34c4a98dc..0f3058e775 100644 --- a/src/calibre/ebooks/rtf2xml/line_endings.py +++ b/src/calibre/ebooks/rtf2xml/line_endings.py @@ -18,7 +18,7 @@ from calibre.utils.cleantext import clean_ascii_chars class FixLineEndings: - """Fix line endings""" + '''Fix line endings''' def __init__(self, bug_handler, @@ -50,6 +50,6 @@ class FixLineEndings: # copy copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "line_endings.data") + copy_obj.copy_file(self.__write_to, 'line_endings.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/list_numbers.py b/src/calibre/ebooks/rtf2xml/list_numbers.py index d9379087ad..1433751cbe 100644 --- a/src/calibre/ebooks/rtf2xml/list_numbers.py +++ b/src/calibre/ebooks/rtf2xml/list_numbers.py @@ -19,10 +19,10 @@ from . import open_for_read, open_for_write class ListNumbers: - """ + ''' RTF puts list numbers outside of the paragraph. The public method in this class put the list numbers inside the paragraphs. - """ + ''' def __init__(self, in_file, @@ -46,14 +46,14 @@ class ListNumbers: self.__write_to = better_mktemp() def __initiate_values(self): - """ + ''' initiate values for fix_list_numbers. Required: Nothing Return: Nothing - """ - self.__state = "default" + ''' + self.__state = 'default' self.__list_chunk = '' self.__previous_line = '' self.__list_text_ob_count = '' @@ -65,13 +65,13 @@ class ListNumbers: } def __after_ob_func(self, line): - """ + ''' Handle the line immediately after an open bracket. Required: self, line Returns: Nothing - """ + ''' if self.__token_info == 'cw<ls<list-text_': self.__state = 'list_text' self.__list_chunk = self.__list_chunk + \ @@ -84,10 +84,10 @@ class ListNumbers: self.__state = 'default' def __after_list_text_func(self, line): - """ + ''' Look for an open bracket or a line of text, and then print out the self.__list_chunk. Print out the line. - """ + ''' if line[0:2] == 'ob' or line[0:2] == 'tx': self.__state = 'default' self.__write_obj.write('mi<mk<lst-txbeg_\n') @@ -103,15 +103,15 @@ class ListNumbers: self.__write_obj.write(line) def __determine_list_type(self, chunk): - """ + ''' Determine if the list is ordered or itemized - """ + ''' lines = chunk.split('\n') text_string = '' for line in lines: if line[0:5] == 'tx<hx': - if line[17:] == '\'B7': - return "unordered" + if line[17:] == "'B7": + return 'unordered' elif line[0:5] == 'tx<nu': text_string += line[17:] text_string = text_string.replace('.', '') @@ -119,16 +119,16 @@ class ListNumbers: text_string = text_string.replace(')', '') if text_string.isdigit(): return 'ordered' - """ + ''' sys.stderr.write('module is list_numbers\n') sys.stderr.write('method is __determine type\n') sys.stderr.write('Couldn\'t get type of list\n') - """ + ''' # must be some type of ordered list -- just a guess! return 'unordered' def __list_text_func(self, line): - """ + ''' Handle lines that are part of the list text. If the end of the list text is found (the closing bracket matches the self.__list_text_ob), then change the state. Always add the line to the self.__list_chunk @@ -136,7 +136,7 @@ class ListNumbers: self, line Returns: Nothing - """ + ''' if self.__list_text_ob == self.__cb_count: self.__state = 'after_list_text' self.__right_after_list_text = 1 @@ -146,7 +146,7 @@ class ListNumbers: self.__list_chunk = self.__list_chunk + line def __default_func(self, line): - """ + ''' Handle the lines that are not part of any special state. Look for an opening bracket. If an open bracket is found, add this line to a temporary self.__previous line, which other methods need. Otherwise, @@ -155,7 +155,7 @@ class ListNumbers: self, line Returns: Nothing - """ + ''' if self.__token_info == 'ob<nu<open-brack': self.__state = 'after_ob' self.__previous_line = line @@ -197,6 +197,6 @@ class ListNumbers: self.__write_obj.close() copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "list_numbers.data") + copy_obj.copy_file(self.__write_to, 'list_numbers.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/list_table.py b/src/calibre/ebooks/rtf2xml/list_table.py index 06b9f6b42a..675950740b 100644 --- a/src/calibre/ebooks/rtf2xml/list_table.py +++ b/src/calibre/ebooks/rtf2xml/list_table.py @@ -13,10 +13,10 @@ class ListTable: - """ + ''' Parse the list table line. Make a string. Form a dictionary. Return the string and the dictionary. - """ + ''' def __init__( self, @@ -68,23 +68,23 @@ class ListTable: 'cw<ci<bold______' : 'bold', 'cw<ss<para-style' : 'paragraph-style-name', } - """ + ''' all_lists = [{anything here?} [{list-templateid = ""} [{level-indent}],[{level-indent}] ] ], - """ + ''' def __parse_lines(self, line): - """ + ''' Required : line --line to parse Returns: nothing Logic: Split the lines into a list by a new line. Process the line according to the state. - """ + ''' lines = line.split('\n') self.__ob_count = 0 self.__ob_group = 0 @@ -104,18 +104,18 @@ class ListTable: # self.__add_to_final_line() def __default_func(self, line): - """ + ''' Requires: line --line to process Return: nothing Logic: This state is used at the start and end of a list. Look for an opening bracket, which marks the change of state. - """ + ''' if self.__token_info == 'ob<nu<open-brack': self.__state = 'unsure_ob' def __found_list_func(self, line): - """ + ''' Requires: line -- line to process Returns: nothing Logic: @@ -127,7 +127,7 @@ class ListTable: "list-id" and the value of an empty list. Later, this empty list will be filled with all the ids for which the formatting is valid. Append the temporary dictionary to the new list. - """ + ''' self.__state = 'list' self.__list_ob_count = self.__ob_count self.__all_lists.append([]) @@ -135,14 +135,14 @@ class ListTable: self.__all_lists[-1].append(the_dict) def __list_func(self, line): - """ + ''' Requires: line --line to process Returns: nothing Logic: This method is called when you are in a list, but outside of a level. Check for the end of the list. Otherwise, use the self.__mainlist_dict to determine if you need to add a lines values to the main list. - """ + ''' if self.__token_info == 'cb<nu<clos-brack' and\ self.__cb_count == self.__list_ob_count: self.__state = 'default' @@ -157,7 +157,7 @@ class ListTable: self.__all_lists[-1][0][att] = value def __found_level_func(self, line): - """ + ''' Requires: line -- line to process Returns: nothing Logic: @@ -176,7 +176,7 @@ class ListTable: self.__all_lists[-1][0] => a dictionary of the list attributes self.__all_lists[-1][-1] => a list with just a dictionary self.__all_lists[-1][-1][0] => the dictionary of level attributes - """ + ''' self.__state = 'level' self.__level_ob_count = self.__ob_count self.__all_lists[-1].append([]) @@ -185,7 +185,7 @@ class ListTable: self.__level_dict def __level_func(self, line): - """ + ''' Requires: line -- line to parse Returns: @@ -194,7 +194,7 @@ class ListTable: Look for the end of the this group. Change states if an open bracket is found. Add attributes to all_dicts if an appropriate token is found. - """ + ''' if self.__token_info == 'cb<nu<clos-brack' and\ self.__cb_count == self.__level_ob_count: self.__state = 'list' @@ -207,7 +207,7 @@ class ListTable: self.__all_lists[-1][-1][0][att] = value def __level_number_func(self, line): - """ + ''' Requires: line -- line to process Returns: @@ -219,7 +219,7 @@ class ListTable: this by 2 and round it. Remove the ".0". Sandwwhich the result to give you something like level1-show-level. The show-level attribute means the numbering for this level. - """ + ''' if self.__token_info == 'cb<nu<clos-brack' and\ self.__cb_count == self.__level_number_ob_count: self.__state = 'level' @@ -229,14 +229,14 @@ class ListTable: self.__level_numbers_string += '\\'%s' % line[18:] elif self.__token_info == 'tx<nu<__________': self.__level_numbers_string += line[17:] - """ + ''' num = line[18:] num = int(num, 16) level = str(round((num - 1)/2, 0)) level = level[:-2] level = 'level%s-show-level' % level self.__all_lists[-1][-1][0][level] = 'true' - """ + ''' def __level_text_func(self, line): """ @@ -278,14 +278,14 @@ class ListTable: self.__all_lists[-1][-1][0]['level-template-id'] = value def __parse_level_text_length(self, line): - """ + ''' Requires: line --line with hexadecimal number Returns: nothing Logic: Method is used for to parse text in the \\leveltext group. - """ + ''' num = line[18:] the_num = int(num, 16) if not self.__found_level_text_length: @@ -304,20 +304,20 @@ class ListTable: self.__prefix_string = None def __list_name_func(self, line): - """ + ''' Requires: line --line to process Returns: nothing Logic: Simply check for the end of the group and change states. - """ + ''' if self.__token_info == 'cb<nu<clos-brack' and\ self.__cb_count == self.__list_name_ob_count: self.__state = 'list' def __after_bracket_func(self, line): - """ + ''' Requires: line --line to parse Returns: @@ -327,7 +327,7 @@ class ListTable: you are now in. WARNING: this could cause problems. If no group is found, the state will remain unsure_ob, which means no other text will be parsed. - """ + ''' if self.__token_info == 'cw<ls<level-text': self.__state = 'level_text' self.__level_text_ob_count = self.__ob_count @@ -348,9 +348,9 @@ class ListTable: raise self.__bug_handler def __add_to_final_line(self): - """ + ''' Method no longer used. - """ + ''' self.__list_table_final = 'mi<mk<listabbeg_\n' self.__list_table_final += 'mi<tg<open______<list-table\n' + \ 'mi<mk<listab-beg\n' + self.__list_table_final @@ -359,7 +359,7 @@ class ListTable: self.__list_table_final += 'mi<mk<listabend_\n' def __write_final_string(self): - """ + ''' Requires: nothing Returns: @@ -372,7 +372,7 @@ class ListTable: Remove the first item (the dictionary) form this list. Now iterate through what is left in the list. Each list will contain one item, a dictionary. Get this dictionary and print out key => value pair. - """ + ''' not_allow = ['list-id',] id = 0 self.__list_table_final = 'mi<mk<listabbeg_\n' @@ -431,7 +431,7 @@ class ListTable: self.__list_table_final += 'mi<mk<listabend_\n' def parse_list_table(self, line): - """ + ''' Requires: line -- line with border definition in it Returns: @@ -439,6 +439,6 @@ class ListTable: Logic: Call on the __parse_lines method, which splits the text string into lines (which will be tokens) and processes them. - """ + ''' self.__parse_lines(line) return self.__list_table_final, self.__all_lists diff --git a/src/calibre/ebooks/rtf2xml/make_lists.py b/src/calibre/ebooks/rtf2xml/make_lists.py index cd06075e47..cd09e8fac0 100644 --- a/src/calibre/ebooks/rtf2xml/make_lists.py +++ b/src/calibre/ebooks/rtf2xml/make_lists.py @@ -59,7 +59,7 @@ class MakeLists: self.__write_list_info = write_list_info def __initiate_values(self): - """ + ''' Required: Nothing Return: @@ -67,11 +67,11 @@ class MakeLists: Logic: The self.__end_list is a list of tokens that will force a list to end. Likewise, the self.__end_lines is a list of lines that forces a list to end. - """ - self.__state = "default" + ''' + self.__state = 'default' self.__left_indent = 0 self.__list_type = 'not-defined' - self.__pard_def = "" + self.__pard_def = '' self.__all_lists = [] self.__level = 0 self.__list_chunk = '' @@ -128,7 +128,7 @@ class MakeLists: self.__write_obj.write(line) def __after_pard_func(self, line): - """ + ''' Required: line -- the line of current text. Return: @@ -145,7 +145,7 @@ class MakeLists: If a bigger block is found (such as a section or a cell), end all lists. indented. If no special line is found, add each line to a buffer. - """ + ''' if self.__token_info == 'mi<tg<open-att__' and line[17:37] == 'paragraph-definition': is_heading = self.__is_a_heading() # found paragraph definition and not heading 1 @@ -188,7 +188,7 @@ class MakeLists: self.__list_chunk += line def __list_after_par_def_func(self, line, id): - """ + ''' Required: line -- the line of current text. id -- the id of the current list @@ -203,7 +203,7 @@ class MakeLists: If the list id is the same as the last one, check the indent on the current paragraph definition. If it is greater than the previous one, do not end the current list or item. Start a new list. - """ + ''' last_list_id = self.__all_lists[-1]['id'] if id != last_list_id: self.__close_lists() @@ -222,7 +222,7 @@ class MakeLists: self.__list_chunk = '' def __close_lists(self): - """ + ''' Required: Nothing Return: @@ -234,7 +234,7 @@ class MakeLists: Keep track of how many levels you close. Reduce the list by that many levels. Reverse the list again. - """ + ''' if self.__line_num < 25 and self.__found_appt: sys.stderr.write('in closing out lists\n') sys.stderr.write('current_indent is "%s"\n' % self.__left_indent) @@ -253,19 +253,19 @@ class MakeLists: self.__all_lists.reverse() def __write_end_list(self): - """ + ''' Required: Nothing Return: Nothing Logic: Write the end of a list. - """ + ''' self.__write_obj.write('mi<tg<close_____<list\n') self.__write_obj.write('mi<mk<list_close\n') def __write_start_list(self, id): - """ + ''' Required: id -- the id of the current list. Return: @@ -281,7 +281,7 @@ class MakeLists: the first item of what I just got. This is a dictionary. Get the list-id. This is a list. Check to see if the current id is in this list. If so, then get the list-type from the dictionary. - """ + ''' the_dict = {} the_dict['left-indent'] = self.__left_indent the_dict['id'] = id @@ -341,7 +341,7 @@ class MakeLists: self.__write_start_item() def __get_index_of_list(self, id): - """ + ''' Requires: id -- id of current paragraph-definition Returns: @@ -353,7 +353,7 @@ class MakeLists: track of how many times you iterate with the counter. Once you find a match, return the counter. If no match is found, print out an error message. - """ + ''' # some RTF use 0 indexed list. Don't know what to do? if id == '0': return @@ -386,7 +386,7 @@ class MakeLists: self.__write_obj.write('mi<tg<item__end_\n') def __default_func(self, line): - """ + ''' Required: self, line Returns: @@ -395,7 +395,7 @@ class MakeLists: Look for the start of a paragraph definition. If one is found, check if it contains a list-id. If it does, start a list. Change the state to in_pard. - """ + ''' if self.__token_info == 'mi<tg<open-att__' and line[17:37] == 'paragraph-definition': is_a_heading = self.__is_a_heading() if not is_a_heading: @@ -429,20 +429,20 @@ class MakeLists: if self.__token_info == 'mi<mk<list-type_': # <ordered self.__list_type = line[17:-1] if self.__list_type == 'item': - self.__list_type = "unordered" + self.__list_type = 'unordered' def __get_style_name(self, line): if self.__token_info == 'mi<mk<style-name': self.__style_name = line[17:-1] def make_lists(self): - """ + ''' Required: nothing Returns: original file will be changed Logic: - """ + ''' self.__initiate_values() read_obj = open_for_read(self.__file) self.__write_obj = open_for_write(self.__write_to) @@ -460,6 +460,6 @@ class MakeLists: self.__write_obj.close() copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "make_lists.data") + copy_obj.copy_file(self.__write_to, 'make_lists.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/old_rtf.py b/src/calibre/ebooks/rtf2xml/old_rtf.py index be49f7f306..cfc88d0223 100644 --- a/src/calibre/ebooks/rtf2xml/old_rtf.py +++ b/src/calibre/ebooks/rtf2xml/old_rtf.py @@ -16,12 +16,12 @@ from . import open_for_read class OldRtf: - """ + ''' Check to see if the RTF is an older version Logic: If allowable control word/properties happen in text without being enclosed in brackets the file will be considered old rtf - """ + ''' def __init__(self, in_file, bug_handler, @@ -99,13 +99,13 @@ class OldRtf: self.__state = 'in_body' def check_if_old_rtf(self): - """ + ''' Requires: nothing Returns: True if file is older RTf False if file is newer RTF - """ + ''' self.__initiate_values() line_num = 0 with open_for_read(self.__file) as read_obj: diff --git a/src/calibre/ebooks/rtf2xml/options_trem.py b/src/calibre/ebooks/rtf2xml/options_trem.py index c62e18910a..5237802ef8 100644 --- a/src/calibre/ebooks/rtf2xml/options_trem.py +++ b/src/calibre/ebooks/rtf2xml/options_trem.py @@ -46,12 +46,12 @@ class ParseOptions: self.__options_okay = 1 def __make_long_list_func(self, options_dict): - """ + ''' Required: options_dict -- the dictionary mapping options to a list Returns: a list of legal options - """ + ''' legal_list = [] keys = options_dict.keys() for key in keys: @@ -60,12 +60,12 @@ class ParseOptions: return legal_list def __make_short_list_func(self, options_dict): - """ + ''' Required: options_dict --the dictionary mapping options to a list Returns: a list of legal short options - """ + ''' legal_list = [] keys = options_dict.keys() for key in keys: @@ -77,14 +77,14 @@ class ParseOptions: return legal_list def __make_short_long_dict_func(self, options_dict): - """ + ''' Required: options_dict --the dictionary mapping options to a list Returns: a dictionary with keys of short options and values of long options Logic: read through the options dictionary and pair short options with long options - """ + ''' short_long_dict = {} keys = options_dict.keys() for key in keys: @@ -98,12 +98,12 @@ class ParseOptions: return short_long_dict def __make_options_with_arg_list(self, options_dict): - """ + ''' Required: options_dict --the dictionary mapping options to a list Returns: a list of options that take arguments. - """ + ''' opt_with_arg = [] keys = options_dict.keys() for key in keys: @@ -116,14 +116,14 @@ class ParseOptions: return opt_with_arg def __sub_short_with_long(self): - """ + ''' Required: nothing Returns: a new system string Logic: iterate through the system string and replace short options with long options - """ + ''' new_string = [] sub_list = self.__short_long_dict.keys() for item in self.__system_string: @@ -184,7 +184,7 @@ class ParseOptions: return new_system_string def __get_just_options(self): - """ + ''' Requires: nothing Returns: @@ -194,7 +194,7 @@ class ParseOptions: option. The options are everything in the system string before the last option. Check to see that the options contain no arguments. - """ + ''' highest = 0 counter = 0 found_options = 0 @@ -217,7 +217,7 @@ class ParseOptions: return just_options, arguments def __is_legal_option_func(self): - """ + ''' Requires: nothing Returns: @@ -225,7 +225,7 @@ class ParseOptions: Logic: Check each value in the newly creatd options list to see if it matches what the user describes as a legal option. - """ + ''' illegal_options = [] for arg in self.__system_string: if '=' in arg: @@ -282,7 +282,7 @@ if __name__ == '__main__': ) options, the_args = test_obj.parse_options() print(options, the_args) - """ + ''' this_options = ['--foo', '-o'] this_opt_with_args = ['--foo'] - """ + ''' diff --git a/src/calibre/ebooks/rtf2xml/output.py b/src/calibre/ebooks/rtf2xml/output.py index f9f11b4f5c..c7704b1d5b 100644 --- a/src/calibre/ebooks/rtf2xml/output.py +++ b/src/calibre/ebooks/rtf2xml/output.py @@ -19,9 +19,9 @@ from . import open_for_read, open_for_write class Output: - """ + ''' Output file - """ + ''' def __init__(self, file, @@ -46,7 +46,7 @@ class Output: self.__out_file = out_file def output(self): - """ + ''' Required: nothing Returns: @@ -54,7 +54,7 @@ class Output: Logic: output the line to the screen if no output file given. Otherwise, output to the file. - """ + ''' if self.__output_dir: self.__output_to_dir_func() elif self.__out_file: @@ -64,7 +64,7 @@ class Output: self.__output_to_standard_func() def __output_to_dir_func(self): - """ + ''' Requires: nothing Returns: @@ -72,7 +72,7 @@ class Output: Logic: Create a file within the output directory. Read one file at a time. Output line to the newly-created file. - """ + ''' base_name = os.path.basename(self.__orig_file) base_name, ext = os.path.splitext(base_name) output_file = os.path.join(self.__output_dir, '%s.xml' % base_name) @@ -95,28 +95,28 @@ class Output: self.__output_to_standard_func() def __output_to_file_func(self): - """ + ''' Required: nothing Returns: nothing Logic: read one line at a time. Output to standard - """ + ''' with open_for_read(self.__file) as read_obj: with open_for_write(self.__out_file) as write_obj: for line in read_obj: write_obj.write(line) def __output_to_standard_func(self): - """ + ''' Required: nothing Returns: nothing Logic: read one line at a time. Output to standard - """ + ''' with open_for_read(self.__file) as read_obj: for line in read_obj: sys.stdout.write(line) diff --git a/src/calibre/ebooks/rtf2xml/override_table.py b/src/calibre/ebooks/rtf2xml/override_table.py index 8b01f8e4ec..8f72d52b06 100644 --- a/src/calibre/ebooks/rtf2xml/override_table.py +++ b/src/calibre/ebooks/rtf2xml/override_table.py @@ -13,13 +13,13 @@ class OverrideTable: - """ + ''' Parse a line of text to make the override table. Return a string (which will convert to XML) and the dictionary containing all the information about the lists. This dictionary is the result of the dictionary that is first passed to this module. This module modifies the dictionary, assigning lists numbers to each list. - """ + ''' def __init__( self, @@ -45,7 +45,7 @@ class OverrideTable: } def __override_func(self, line): - """ + ''' Requires: line -- line to parse Returns: @@ -54,7 +54,7 @@ class OverrideTable: The group {\\override has been found. Check for the end of the group. Otherwise, add appropriate tokens to the override dictionary. - """ + ''' if self.__token_info == 'cb<nu<clos-brack' and\ self.__cb_count == self.__override_ob_count: self.__state = 'default' @@ -66,7 +66,7 @@ class OverrideTable: self.__override_list[-1][att] = value def __parse_override_dict(self): - """ + ''' Requires: nothing Returns: @@ -84,7 +84,7 @@ class OverrideTable: [[{list-id:[HERE!],[{}]] This is a list, since one list in the table in the preamble of RTF can apply to multiple lists in the body. - """ + ''' override_dict = self.__override_list[-1] list_id = override_dict.get('list-id') if list_id is None and self.__level > 3: @@ -104,7 +104,7 @@ class OverrideTable: counter += 1 def __parse_lines(self, line): - """ + ''' Requires: line --ine to parse Returns: @@ -112,7 +112,7 @@ class OverrideTable: Logic: Break the into tokens by splitting it on the newline. Call on the method according to the state. - """ + ''' lines = line.split('\n') self.__ob_count = 0 self.__ob_group = 0 @@ -132,19 +132,19 @@ class OverrideTable: # self.__add_to_final_line() def __default_func(self, line): - """ + ''' Requires: line -- line to parse Return: nothing Logic: Look for an open bracket and change states when found. - """ + ''' if self.__token_info == 'ob<nu<open-brack': self.__state = 'unsure_ob' def __after_bracket_func(self, line): - """ + ''' Requires: line -- line to parse Returns: @@ -156,7 +156,7 @@ class OverrideTable: state will remain unsure_ob, which means no other text will be parsed. I should do states by a list and simply pop this unsure_ob state to get the previous state. - """ + ''' if self.__token_info == 'cw<ls<lis-overid': self.__state = 'override' self.__override_ob_count = self.__ob_count @@ -168,7 +168,7 @@ class OverrideTable: raise self.__bug_handler(msg) def __write_final_string(self): - """ + ''' Requires: line -- line to parse Returns: @@ -178,7 +178,7 @@ class OverrideTable: Iteratere through the dictionaries in the main override_list. For each dictionary, write an empty tag "override-list". Add the attributes and values of the tag from the dictionary. - """ + ''' self.__override_table_final = 'mi<mk<over_beg_\n' self.__override_table_final += 'mi<tg<open______<override-table\n' + \ 'mi<mk<overbeg__\n' + self.__override_table_final @@ -195,13 +195,13 @@ class OverrideTable: self.__override_table_final += 'mi<mk<overribend_\n' def parse_override_table(self, line): - """ + ''' Requires: line -- line with border definition in it Returns: A string that will be converted to XML, and a dictionary of all the properties of the RTF lists. Logic: - """ + ''' self.__parse_lines(line) return self.__override_table_final, self.__list_of_lists diff --git a/src/calibre/ebooks/rtf2xml/paragraph_def.py b/src/calibre/ebooks/rtf2xml/paragraph_def.py index 486690a30d..26619868da 100644 --- a/src/calibre/ebooks/rtf2xml/paragraph_def.py +++ b/src/calibre/ebooks/rtf2xml/paragraph_def.py @@ -79,9 +79,9 @@ if another paragraph_def is found, the state changes to collect_tokens. self.__write_to = better_mktemp() def __initiate_values(self): - """ + ''' Initiate all values. - """ + ''' # Dictionary needed to convert shortened style names to readable names self.__token_dict={ # paragraph formatting => pf @@ -307,14 +307,14 @@ if another paragraph_def is found, the state changes to collect_tokens. } def __before_1st_para_def_func(self, line): - """ + ''' Required: line -- line to parse Returns: nothing Logic: Look for the beginning of a paragraph definition - """ + ''' # cw<pf<par-def___<nu<true if self.__token_info == 'cw<pf<par-def___': self.__found_para_def_func() @@ -328,7 +328,7 @@ if another paragraph_def is found, the state changes to collect_tokens. self.__reset_dict() def __collect_tokens_func(self, line): - """ + ''' Required: line --line to parse Returns: @@ -341,7 +341,7 @@ if another paragraph_def is found, the state changes to collect_tokens. change the state to after_para_def. Otherwise, check if the token is a paragraph definition word; if so, add it to the attributes and values dictionary. - """ + ''' action = self.__collect_tokens_dict.get(self.__token_info) if action: action(line) @@ -360,15 +360,15 @@ if another paragraph_def is found, the state changes to collect_tokens. self.__att_val_dict[token] = line[20:-1] def __tab_stop_func(self, line): - """ - """ + ''' + ''' self.__att_val_dict['tabs'] += '%s:' % self.__tab_type self.__att_val_dict['tabs'] += '%s;' % line[20:-1] self.__tab_type = 'left' def __tab_type_func(self, line): - """ - """ + ''' + ''' type = self.__tab_type_dict.get(self.__token_info) if type is not None: self.__tab_type = type @@ -378,8 +378,8 @@ if another paragraph_def is found, the state changes to collect_tokens. raise self.__bug_handler(msg) def __tab_leader_func(self, line): - """ - """ + ''' + ''' leader = self.__tab_type_dict.get(self.__token_info) if leader is not None: self.__att_val_dict['tabs'] += '%s^' % leader @@ -389,14 +389,14 @@ if another paragraph_def is found, the state changes to collect_tokens. raise self.__bug_handler(msg) def __tab_bar_func(self, line): - """ - """ + ''' + ''' # self.__att_val_dict['tabs-bar'] += '%s:' % line[20:-1] self.__att_val_dict['tabs'] += 'bar:%s;' % (line[20:-1]) self.__tab_type = 'left' def __parse_border(self, line): - """ + ''' Requires: line --line to parse Returns: @@ -404,12 +404,12 @@ if another paragraph_def is found, the state changes to collect_tokens. Logic: Uses the border_parse module to return a dictionary of attribute value pairs for a border line. - """ + ''' border_dict = self.__border_obj.parse_border(line) self.__att_val_dict.update(border_dict) def __para_def_in_para_def_func(self, line): - """ + ''' Requires: line --line to parse Returns: @@ -417,7 +417,7 @@ if another paragraph_def is found, the state changes to collect_tokens. Logic: I have found a \\pard while I am collecting tokens. I want to reset the dectionary and do nothing else. - """ + ''' # Change this self.__state = 'collect_tokens' self.__reset_dict() @@ -456,7 +456,7 @@ if another paragraph_def is found, the state changes to collect_tokens. self.__state = 'in_paragraphs' def __after_para_def_func(self, line): - """ + ''' Requires: line -- line to parse Returns: @@ -464,7 +464,7 @@ if another paragraph_def is found, the state changes to collect_tokens. Logic: Check if the token info is the start of a paragraph. If so, call on the function found in the value of the dictionary. - """ + ''' action = self.__after_para_def_dict.get(self.__token_info) if self.__token_info == 'cw<pf<par-def___': self.__found_para_def_func() @@ -474,14 +474,14 @@ if another paragraph_def is found, the state changes to collect_tokens. self.__write_obj.write(line) def __in_paragraphs_func(self, line): - """ + ''' Requires: line --current line Returns: nothing Logic: Look for the end of a paragraph, the start of a cell or row. - """ + ''' action = self.__in_paragraphs_dict.get(self.__token_info) if action: action(line) @@ -489,7 +489,7 @@ if another paragraph_def is found, the state changes to collect_tokens. self.__write_obj.write(line) def __found_para_end_func(self,line): - """ + ''' Requires: line -- line to print out Returns: @@ -498,7 +498,7 @@ if another paragraph_def is found, the state changes to collect_tokens. State is in paragraphs. You have found the end of a paragraph. You need to print out the line and change the state to after paragraphs. - """ + ''' self.__state = 'after_para_end' self.__write_obj.write(line) @@ -575,7 +575,7 @@ if another paragraph_def is found, the state changes to collect_tokens. self.__state = 'after_para_def' def __write_para_def_end_func(self): - """ + ''' Requires: nothing Returns: @@ -584,7 +584,7 @@ if another paragraph_def is found, the state changes to collect_tokens. Print out the end of the pargraph definition tag, and the markers that let me know when I have reached this tag. (These markers are used for later parsing.) - """ + ''' self.__write_obj.write(self.__end2_marker) self.__write_obj.write('mi<tg<close_____<paragraph-definition\n') self.__write_obj.write(self.__end_marker) @@ -597,14 +597,14 @@ if another paragraph_def is found, the state changes to collect_tokens. self.__write_obj.write('mi<mk<caps-end__\n') def __get_num_of_style(self): - """ + ''' Requires: nothing Returns: nothing Logic: Get a unique value for each style. - """ + ''' my_string = '' new_style = 0 # when determining uniqueness for a style, ingorne these values, since @@ -644,7 +644,7 @@ if another paragraph_def is found, the state changes to collect_tokens. self.__body_style_strings.append(style_string) def __write_para_def_beg(self): - """ + ''' Requires: nothing Returns: @@ -653,7 +653,7 @@ if another paragraph_def is found, the state changes to collect_tokens. Print out the beginning of the pargraph definition tag, and the markers that let me know when I have reached this tag. (These markers are used for later parsing.) - """ + ''' self.__get_num_of_style() table = self.__att_val_dict.get('in-table') if table: @@ -676,13 +676,13 @@ if another paragraph_def is found, the state changes to collect_tokens. self.__write_obj.write('<style-number>%s' % self.__att_val_dict['style-num']) tabs_list = ['tabs-left', 'tabs-right', 'tabs-decimal', 'tabs-center', 'tabs-bar', 'tabs'] - """ + ''' for tab_item in tabs_list: if self.__att_val_dict[tab_item] != '': the_value = self.__att_val_dict[tab_item] the_value = the_value[:-1] self.__write_obj.write('<%s>%s' % (tab_item, the_value)) - """ + ''' if self.__att_val_dict['tabs'] != '': the_value = self.__att_val_dict['tabs'] # the_value = the_value[:-1] @@ -707,7 +707,7 @@ if another paragraph_def is found, the state changes to collect_tokens. self.__state = 'after_para_def' def __reset_dict(self): - """ + ''' Requires: nothing Returns: @@ -715,7 +715,7 @@ if another paragraph_def is found, the state changes to collect_tokens. Logic: The dictionary containing values and attributes must be reset each time a new paragraphs definition is found. - """ + ''' self.__att_val_dict.clear() self.__att_val_dict['name'] = 'Normal' self.__att_val_dict['font-style'] = self.__default_font @@ -728,7 +728,7 @@ if another paragraph_def is found, the state changes to collect_tokens. self.__att_val_dict['tabs'] = '' def make_paragraph_def(self): - """ + ''' Requires: nothing Returns: @@ -736,7 +736,7 @@ if another paragraph_def is found, the state changes to collect_tokens. Logic: Read one line in at a time. Determine what action to take based on the state. - """ + ''' self.__initiate_values() read_obj = open_for_read(self.__file) self.__write_obj = open_for_write(self.__write_to) @@ -754,7 +754,7 @@ if another paragraph_def is found, the state changes to collect_tokens. self.__write_obj.close() copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "paragraphs_def.data") + copy_obj.copy_file(self.__write_to, 'paragraphs_def.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) return self.__body_style_strings diff --git a/src/calibre/ebooks/rtf2xml/paragraphs.py b/src/calibre/ebooks/rtf2xml/paragraphs.py index a7786c4ea1..a642bba4c3 100644 --- a/src/calibre/ebooks/rtf2xml/paragraphs.py +++ b/src/calibre/ebooks/rtf2xml/paragraphs.py @@ -68,9 +68,9 @@ class Paragraphs: self.__write_to = better_mktemp() def __initiate_values(self): - """ + ''' Initiate all values. - """ + ''' self.__state = 'before_body' self.__start_marker = 'mi<mk<para-start\n' # outside para tags self.__start2_marker = 'mi<mk<par-start_\n' # inside para tags @@ -122,7 +122,7 @@ class Paragraphs: self.__write_obj.write(line) def __not_paragraph_func(self, line): - """ + ''' Required: line --line to parse Returns: @@ -131,14 +131,14 @@ class Paragraphs: This function handles all lines that are outside of the paragraph. It looks for clues that start a paragraph, and when found, switches states and writes the start tags. - """ + ''' action = self.__not_paragraph_dict.get(self.__token_info) if action: action(line) self.__write_obj.write(line) def __paragraph_func(self, line): - """ + ''' Required: line --line to parse Returns: @@ -148,7 +148,7 @@ class Paragraphs: looks for clues to the end of the paragraph. When a clue is found, it calls on another method to write the end of the tag and change the state. - """ + ''' action = self.__paragraph_dict.get(self.__token_info) if action: action(line) @@ -156,7 +156,7 @@ class Paragraphs: self.__write_obj.write(line) def __start_para_func(self, line): - """ + ''' Requires: line --line to parse Returns: @@ -164,7 +164,7 @@ class Paragraphs: Logic: This function writes the beginning tags for a paragraph and changes the state to paragraph. - """ + ''' self.__write_obj.write(self.__start_marker) # marker for later parsing self.__write_obj.write( 'mi<tg<open______<para\n' @@ -173,7 +173,7 @@ class Paragraphs: self.__state = 'paragraph' def __empty_para_func(self, line): - """ + ''' Requires: line --line to parse Returns: @@ -181,7 +181,7 @@ class Paragraphs: Logic: This function writes the empty tags for a paragraph. It does not do anything if self.__write_empty_para is 0. - """ + ''' if self.__write_empty_para: self.__write_obj.write(self.__start_marker) # marker for later parsing self.__write_obj.write( @@ -190,20 +190,20 @@ class Paragraphs: self.__write_obj.write(self.__end_marker) # marker for later parsing def __empty_pgbk_func(self, line): - """ + ''' Requires: line --line to parse Returns: nothing Logic: This function writes the empty tags for a page break. - """ + ''' self.__write_obj.write( 'mi<tg<empty_____<page-break\n' ) def __close_para_func(self, line): - """ + ''' Requires: line --line to parse Returns: @@ -211,7 +211,7 @@ class Paragraphs: Logic: This function writes the end tags for a paragraph and changes the state to not_paragraph. - """ + ''' self.__write_obj.write(self.__end2_marker) # marker for later parser self.__write_obj.write( 'mi<tg<close_____<para\n' @@ -221,14 +221,14 @@ class Paragraphs: self.__state = 'not_paragraph' def __bogus_para__def_func(self, line): - """ + ''' Requires: line --line to parse Returns: nothing Logic: if a \\pard occurs in a paragraph, I want to ignore it. (I believe) - """ + ''' self.__write_obj.write('mi<mk<bogus-pard\n') def make_paragraphs(self): @@ -259,6 +259,6 @@ class Paragraphs: action(line) copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "paragraphs.data") + copy_obj.copy_file(self.__write_to, 'paragraphs.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/pict.py b/src/calibre/ebooks/rtf2xml/pict.py index 46c8f23a06..4be35f44a8 100644 --- a/src/calibre/ebooks/rtf2xml/pict.py +++ b/src/calibre/ebooks/rtf2xml/pict.py @@ -20,7 +20,7 @@ from . import open_for_read, open_for_write class Pict: - """Process graphic information""" + '''Process graphic information''' def __init__(self, in_file, bug_handler, @@ -52,17 +52,17 @@ class Pict: } def __open_br_func(self, line): - return "{\n" + return '{\n' def __close_br_func(self, line): - return "}\n" + return '}\n' def __text_func(self, line): # tx<nu<__________<true text return line[17:] def __make_dir(self): - """ Make a directory to put the image data in""" + ''' Make a directory to put the image data in''' base_name = os.path.basename(getattr(self.__orig_file, 'name', self.__orig_file)) base_name = os.path.splitext(base_name)[0] @@ -71,7 +71,7 @@ class Pict: self.__out_file)) else: dir_name = os.path.dirname(self.__orig_file) - self.__dir_name = base_name + "_rtf_pict_dir/" + self.__dir_name = base_name + '_rtf_pict_dir/' self.__dir_name = os.path.join(dir_name, self.__dir_name) if not os.path.isdir(self.__dir_name): try: @@ -93,15 +93,15 @@ class Pict: sys.stderr.write('Files removed.\n') def __create_pict_file(self): - """Create a file for all the pict data to be written to. - """ + '''Create a file for all the pict data to be written to. + ''' self.__pict_file = os.path.join(self.__dir_name, 'picts.rtf') self.__write_pic_obj = open_for_write(self.__pict_file, append=True) def __in_pict_func(self, line): if self.__cb_count == self.__pict_br_count: self.__in_pict = False - self.__write_pic_obj.write("}\n") + self.__write_pic_obj.write('}\n') return True else: action = self.__pict_dict.get(self.__token_info) @@ -110,16 +110,16 @@ class Pict: return False def __default(self, line, write_obj): - """Determine if each token marks the beginning of pict data. + '''Determine if each token marks the beginning of pict data. If it does, create a new file to write data to (if that file has not already been created.) Set the self.__in_pict flag to true. If the line does not contain pict data, return 1 - """ - """ + ''' + ''' $pict_count++; $pict_count = sprintf("%03d", $pict_count); print OUTPUT "dv<xx<em<nu<pict<at<num>$pict_count\n"; - """ + ''' if self.__token_info == 'cw<gr<picture___': self.__pict_count += 1 # write_obj.write("mi<tg<em<at<pict<num>%03d\n" % self.__pict_count) @@ -133,16 +133,16 @@ class Pict: self.__in_pict = 1 self.__pict_br_count = self.__ob_count self.__cb_count = 0 - self.__write_pic_obj.write("{\\pict\n") + self.__write_pic_obj.write('{\\pict\n') return False return True def __print_rtf_header(self): - """Print to pict file the necessary RTF data for the file to be + '''Print to pict file the necessary RTF data for the file to be recognized as an RTF file. - """ - self.__write_pic_obj.write("{\\rtf1 \n{\\fonttbl\\f0\\null;} \n") - self.__write_pic_obj.write("{\\colortbl\\red255\\green255\\blue255;} \n\\pard \n") + ''' + self.__write_pic_obj.write('{\\rtf1 \n{\\fonttbl\\f0\\null;} \n') + self.__write_pic_obj.write('{\\colortbl\\red255\\green255\\blue255;} \n\\pard \n') def process_pict(self): self.__make_dir() @@ -163,13 +163,13 @@ class Pict: if to_print : write_obj.write(line) if self.__already_found_pict: - self.__write_pic_obj.write("}\n") + self.__write_pic_obj.write('}\n') self.__write_pic_obj.close() copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "pict.data") + copy_obj.copy_file(self.__write_to, 'pict.data') try: - copy_obj.copy_file(self.__pict_file, "pict.rtf") + copy_obj.copy_file(self.__pict_file, 'pict.rtf') except: pass copy_obj.rename(self.__write_to, self.__file) diff --git a/src/calibre/ebooks/rtf2xml/preamble_div.py b/src/calibre/ebooks/rtf2xml/preamble_div.py index 92c5ddb01f..506797aa54 100644 --- a/src/calibre/ebooks/rtf2xml/preamble_div.py +++ b/src/calibre/ebooks/rtf2xml/preamble_div.py @@ -20,9 +20,9 @@ from . import open_for_read, open_for_write class PreambleDiv: - """ + ''' Break the preamble into divisions. - """ + ''' def __init__(self, in_file, bug_handler, @@ -48,9 +48,9 @@ class PreambleDiv: self.__run_level = run_level def __initiate_values(self): - """ + ''' Set values, including those for the dictionary. - """ + ''' self.__all_lists = {} self.__page = { 'margin-top' : 72, @@ -130,10 +130,10 @@ class PreambleDiv: ) def __ignore_func(self, line): - """ + ''' Ignore all lines, until the bracket is found that marks the end of the group. - """ + ''' if self.__ignore_num == self.__cb_count: self.__state = self.__previous_state @@ -163,9 +163,9 @@ class PreambleDiv: self.__rtf_final = self.__rtf_final + line def __make_default_font_table(self): - """ + ''' If not font table is found, need to write one out. - """ + ''' self.__font_table_final = 'mi<tg<open______<font-table\n' self.__font_table_final += 'mi<mk<fonttb-beg\n' self.__font_table_final += 'mi<mk<fontit-beg\n' @@ -176,9 +176,9 @@ class PreambleDiv: self.__font_table_final += 'mi<tg<close_____<font-table\n' def __make_default_color_table(self): - """ + ''' If no color table is found, write a string for a default one - """ + ''' self.__color_table_final = 'mi<tg<open______<color-table\n' self.__color_table_final += 'mi<mk<clrtbl-beg\n' self.__color_table_final += 'cw<ci<red_______<nu<00\n' @@ -188,10 +188,10 @@ class PreambleDiv: self.__color_table_final += 'mi<tg<close_____<color-table\n' def __make_default_style_table(self): - """ + ''' If not font table is found, make a string for a default one - """ - """ + ''' + ''' self.__style_sheet_final = 'mi<tg<open______<style-table\n' self.__style_sheet_final += self.__style_sheet_final += @@ -199,8 +199,8 @@ class PreambleDiv: self.__style_sheet_final += self.__style_sheet_final += self.__style_sheet_final += 'mi<tg<close_____<style-table\n' - """ - self.__style_sheet_final = """mi<tg<open______<style-table + ''' + self.__style_sheet_final = '''mi<tg<open______<style-table mi<mk<styles-beg mi<mk<stylei-beg cw<ci<font-style<nu<0 @@ -212,7 +212,7 @@ tx<nu<__________<Default Paragraph Font; mi<mk<stylei-end mi<mk<styles-end mi<tg<close_____<style-table -""" +''' def __found_font_table_func(self, line): if self.__found_font_table: @@ -225,13 +225,13 @@ mi<tg<close_____<style-table self.__found_font_table = 1 def __font_table_func(self, line): - """ + ''' Keep adding to the self.__individual_font string until end of group found. If a bracket is found, check that it is only one bracket deep. If it is, then set the marker for an individual font. If it is not, then ignore all data in this group. cw<ci<font-style<nu<0 - """ + ''' if self.__cb_count == self.__close_group_count: self.__state = 'preamble' self.__font_table_final = 'mi<tg<open______<font-table\n' + \ @@ -268,7 +268,7 @@ cw<ci<font-style<nu<0 self.__font_table_final += line def __old_font_func(self, line): - """ + ''' Required: line --line to parse Returns: @@ -277,14 +277,14 @@ cw<ci<font-style<nu<0 used for older forms of RTF: \f3\fswiss\fcharset77 Helvetica-Oblique;\f4\fnil\fcharset77 Geneva;} Note how each font is not divided by a bracket - """ + ''' def __found_color_table_func(self, line): - """ + ''' all functions that start with __found operate the same. They set the state, initiate a string, determine the self.__close_group_count, and set self.__cb_count to zero. - """ + ''' self.__state = 'color_table' self.__color_table_final = '' self.__close_group_count = self.__ob_count @@ -307,9 +307,9 @@ cw<ci<font-style<nu<0 self.__cb_count = 0 def __style_sheet_func(self, line): - """ + ''' Same logic as the font_table_func. - """ + ''' if self.__cb_count == self.__close_group_count: self.__state = 'preamble' self.__style_sheet_final = 'mi<tg<open______<style-table\n' + \ @@ -408,10 +408,10 @@ cw<ci<font-style<nu<0 self.__doc_info_table_final += line def __margin_func(self, line): - """ + ''' Handles lines that describe page info. Add the appropriate info in the token to the self.__margin_dict dictionary. - """ + ''' info = line[6:16] changed = self.__margin_dict.get(info) if changed is None: @@ -430,10 +430,10 @@ cw<ci<font-style<nu<0 # mi<tg<open-att__<footn def __print_sec_info(self): - """ + ''' Check if there is any section info. If so, print it out. If not, print out an empty tag to satisfy the dtd. - """ + ''' if len(self.__section.keys()) == 0: self.__write_obj.write( 'mi<tg<open______<section-definition\n' @@ -449,10 +449,10 @@ cw<ci<font-style<nu<0 self.__write_obj.write('\n') def __section_func(self, line): - """ + ''' Add info pertaining to section to the self.__section dictionary, to be printed out later. - """ + ''' info = self.__translate_sec.get(line[6:16]) if info is None: sys.stderr.write('woops!\n') @@ -475,11 +475,11 @@ cw<ci<font-style<nu<0 self.__write_obj.write(line) def __text_func(self, line): - """ + ''' If the cb_count is less than 1, you have hit the body For older RTF Newer RTF should never have to use this function - """ + ''' if self.__cb_count == '': cb_count = '0002' else: @@ -502,9 +502,9 @@ cw<ci<font-style<nu<0 self.__write_obj.write(line) def __new_section_func(self, line): - """ + ''' This is new. The start of a section marks the end of the preamble - """ + ''' if self.__cb_count == '0002': self.__state = 'body' self.__write_preamble() @@ -515,10 +515,10 @@ cw<ci<font-style<nu<0 self.__write_obj.write(line) def __write_preamble(self): - """ + ''' Write all the strings, which represent all the data in the preamble. Write a body and section beginning. - """ + ''' if self.__no_namespace: self.__write_obj.write( 'mi<tg<open______<doc\n' @@ -555,10 +555,10 @@ cw<ci<font-style<nu<0 self.__write_obj.write('mi<mk<body-open_\n') def __preamble_func(self, line): - """ + ''' Check if the token info belongs to the dictionary. If so, take the appropriate action. - """ + ''' action = self.__state_dict.get(self.__token_info) if action: action(line) @@ -586,7 +586,7 @@ cw<ci<font-style<nu<0 self.__write_obj.close() copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "preamble_div.data") + copy_obj.copy_file(self.__write_to, 'preamble_div.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) return self.__all_lists diff --git a/src/calibre/ebooks/rtf2xml/preamble_rest.py b/src/calibre/ebooks/rtf2xml/preamble_rest.py index 785325581d..bf86ebfdd0 100644 --- a/src/calibre/ebooks/rtf2xml/preamble_rest.py +++ b/src/calibre/ebooks/rtf2xml/preamble_rest.py @@ -19,12 +19,12 @@ from . import open_for_read, open_for_write class Preamble: - """ + ''' Fix the reamaing parts of the preamble. This module does very little. It makes sure that no text gets put in the revision of list table. In the future, when I understand how to interpret the revision table and list table, I will make these methods more functional. - """ + ''' def __init__(self, file, bug_handler, @@ -54,14 +54,14 @@ class Preamble: self.__code_page = code_page self.__platform = platform if temp_dir: - self.__write_to = os.path.join(temp_dir,"info_table_info.data") + self.__write_to = os.path.join(temp_dir,'info_table_info.data') else: - self.__write_to = "info_table_info.data" + self.__write_to = 'info_table_info.data' def __initiate_values(self): - """ + ''' Initiate all values. - """ + ''' self.__state = 'default' self.__text_string = '' self.__state_dict = { @@ -85,7 +85,7 @@ class Preamble: self.__write_obj.write(line) def __found_rtf_head_func(self, line): - """ + ''' Requires: line -- the line to parse Returns: @@ -93,7 +93,7 @@ class Preamble: Logic: Write to the output file the default font info, the code page info, and the platform info. - """ + ''' self.__write_obj.write( 'mi<tg<empty-att_<rtf-definition' '<default-font>%s<code-page>%s' @@ -131,7 +131,7 @@ class Preamble: self.__write_obj.write(line) def fix_preamble(self): - """ + ''' Requires: nothing Returns: @@ -140,7 +140,7 @@ class Preamble: Read one line in at a time. Determine what action to take based on the state. The state can either be default, the revision table, or the list table. - """ + ''' self.__initiate_values() with open_for_read(self.__file) as read_obj: with open_for_write(self.__write_to) as self.__write_obj: @@ -153,6 +153,6 @@ class Preamble: action(line) copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "preamble_div.data") + copy_obj.copy_file(self.__write_to, 'preamble_div.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/process_tokens.py b/src/calibre/ebooks/rtf2xml/process_tokens.py index df0ab9aa04..ce2ce876ec 100644 --- a/src/calibre/ebooks/rtf2xml/process_tokens.py +++ b/src/calibre/ebooks/rtf2xml/process_tokens.py @@ -20,11 +20,11 @@ from . import open_for_read, open_for_write class ProcessTokens: - """ + ''' Process each token on a line and add information that will be useful for later processing. Information will be put on one line, delimited by "<" for main fields, and ">" for sub fields - """ + ''' def __init__(self, in_file, @@ -46,7 +46,7 @@ class ProcessTokens: self.__bug_handler = bug_handler def compile_expressions(self): - self.__num_exp = re.compile(r"([a-zA-Z]+)(.*)") + self.__num_exp = re.compile(r'([a-zA-Z]+)(.*)') self.__utf_exp = re.compile(r'(&.*?;)') def initiate_token_dict(self): @@ -466,7 +466,7 @@ class ProcessTokens: 2060 : 'French Belgium', 11276 : 'French Cameroon', 3084 : 'French Canada', - 12300 : 'French Cote d\'Ivoire', + 12300 : "French Cote d'Ivoire", 5132 : 'French Luxembourg', 13324 : 'French Mali', 6156 : 'French Monaco', @@ -585,7 +585,7 @@ class ProcessTokens: 1024 : 'Unkown', 255 : 'Unkown', } - """ + ''' # unknown # These must get passed on because they occurred after \\* 'do' : ('un', 'unknown___', self.default_func), @@ -609,12 +609,12 @@ class ProcessTokens: 'objdata' : ('un', 'unknown___', self.default_func), 'picprop' : ('un', 'unknown___', self.default_func), 'blipuid' : ('un', 'unknown___', self.default_func), - """ + ''' def __ms_hex_func(self, pre, token, num): num = num[1:] # chop off leading 0, which I added num = num.upper() # the mappings store hex in caps - return 'tx<hx<__________<\'%s\n' % num # add an ' for the mappings + return "tx<hx<__________<'%s\n" % num # add an ' for the mappings def ms_sub_func(self, pre, token, num): return 'tx<mc<__________<%s\n' % token @@ -654,14 +654,14 @@ class ProcessTokens: def __language_func(self, pre, token, num): lang_name = self.__language_dict.get(int(re.search('[0-9]+', num).group())) if not lang_name: - lang_name = "not defined" + lang_name = 'not defined' if self.__run_level > 3: msg = 'No entry for number "%s"' % num raise self.__bug_handler(msg) return f'cw<{pre}<{token}<nu<{lang_name}\n' def two_part_func(self, pre, token, num): - list = token.split("<") + list = token.split('<') token = list[0] num = list[1] return f'cw<{pre}<{token}<nu<{num}\n' @@ -696,7 +696,7 @@ class ProcessTokens: third_field = 'en' num = '%X' % int(num) if len(num) != 2: - num = "0" + num + num = '0' + num return f'cw<{pre}<{token}<{third_field}<{num}\n' # return 'cw<cl<%s<nu<nu<%s>%s<%s\n' % (third_field, token, num, token) @@ -732,7 +732,7 @@ class ProcessTokens: num = '%0.2f' % round(numerator/denominator, 2) return num string_num = str(num) - if string_num[-2:] == ".0": + if string_num[-2:] == '.0': string_num = string_num[:-2] return string_num @@ -754,21 +754,21 @@ class ProcessTokens: return first, second def convert_to_hex(self,number): - """Convert a string to uppercase hexadecimal""" + '''Convert a string to uppercase hexadecimal''' num = int(number) try: - hex_num = "%X" % num + hex_num = '%X' % num return hex_num except: raise self.__bug_handler def process_cw(self, token): - """Change the value of the control word by determining what dictionary - it belongs to""" + '''Change the value of the control word by determining what dictionary + it belongs to''' special = ['*', ':', '}', '{', '~', '_', '-', ';'] # if token != "{" or token != "}": token = token[1:] # strip off leading \ - token = token.replace(" ", "") + token = token.replace(' ', '') # if not token: return only_alpha = token.isalpha() num = None @@ -785,18 +785,18 @@ class ProcessTokens: return 1 def process_tokens(self): - """Main method for handling other methods. """ + '''Main method for handling other methods. ''' line_count = 0 with open_for_read(self.__file) as read_obj: with open_for_write(self.__write_to) as write_obj: for line in read_obj: - token = line.replace("\n", "") + token = line.replace('\n', '') line_count += 1 if line_count == 1 and token != '\\{': - msg = '\nInvalid RTF: document doesn\'t start with {\n' + msg = "\nInvalid RTF: document doesn't start with {\n" raise self.__exception_handler(msg) elif line_count == 2 and token[0:4] != '\\rtf': - msg = '\nInvalid RTF: document doesn\'t start with \\rtf \n' + msg = "\nInvalid RTF: document doesn't start with \\rtf \n" raise self.__exception_handler(msg) the_index = token.find('\\ ') @@ -804,7 +804,7 @@ class ProcessTokens: msg = '\nInvalid RTF: token "\\ " not valid.\nError at line %d'\ % line_count raise self.__exception_handler(msg) - elif token[:1] == "\\": + elif token[:1] == '\\': line = self.process_cw(token) if line is not None: write_obj.write(line) @@ -824,7 +824,7 @@ class ProcessTokens: copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "processed_tokens.data") + copy_obj.copy_file(self.__write_to, 'processed_tokens.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/replace_illegals.py b/src/calibre/ebooks/rtf2xml/replace_illegals.py index c2521922e6..c81af4bb4b 100644 --- a/src/calibre/ebooks/rtf2xml/replace_illegals.py +++ b/src/calibre/ebooks/rtf2xml/replace_illegals.py @@ -20,9 +20,9 @@ from . import open_for_read, open_for_write class ReplaceIllegals: - """ + ''' reaplace illegal lower ascii characters - """ + ''' def __init__(self, in_file, @@ -35,14 +35,14 @@ class ReplaceIllegals: self.__write_to = better_mktemp() def replace_illegals(self): - """ - """ + ''' + ''' with open_for_read(self.__file) as read_obj: with open_for_write(self.__write_to) as write_obj: for line in read_obj: write_obj.write(clean_ascii_chars(line)) copy_obj = copy.Copy() if self.__copy: - copy_obj.copy_file(self.__write_to, "replace_illegals.data") + copy_obj.copy_file(self.__write_to, 'replace_illegals.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/sections.py b/src/calibre/ebooks/rtf2xml/sections.py index 16c000f3be..b54dc1206b 100644 --- a/src/calibre/ebooks/rtf2xml/sections.py +++ b/src/calibre/ebooks/rtf2xml/sections.py @@ -77,9 +77,9 @@ class Sections: self.__write_to = better_mktemp() def __initiate_values(self): - """ + ''' Initiate all values. - """ + ''' self.__mark_start = 'mi<mk<sect-start\n' self.__mark_end = 'mi<mk<sect-end__\n' self.__in_field = 0 @@ -127,7 +127,7 @@ class Sections: } def __found_section_def_func(self, line): - """ + ''' Required: line -- the line to parse Returns: @@ -136,12 +136,12 @@ class Sections: I have found a section definition. Change the state to setion_def (so subsequent lines will be processesed as part of the section definition), and clear the section_values dictionary. - """ + ''' self.__state = 'section_def' self.__section_values.clear() def __attribute_func(self, line, name): - """ + ''' Required: line -- the line to be parsed name -- the changed, readable name (as opposed to the @@ -153,13 +153,13 @@ class Sections: can retrieve it later. The attribute (or key) is the name; the value is the last part of the text string. ex: cw<tb<columns___<nu<2 - """ + ''' attribute = name value = line[20:-1] self.__section_values[attribute] = value def __found_section_func(self, line): - """ + ''' Requires: line -- the line to parse Returns: @@ -167,13 +167,13 @@ class Sections: Logic: I have found the beginning of a section, so change the state accordingly. Also add one to the section counter. - """ + ''' self.__state = 'section' self.__write_obj.write(line) self.__section_num += 1 def __found_section_def_bef_sec_func(self, line): - """ + ''' Requires: line -- the line to parse Returns: @@ -181,25 +181,25 @@ class Sections: Logic: I have found the beginning of a section, so change the state accordingly. Also add one to the section counter. - """ + ''' self.__section_num += 1 self.__found_section_def_func(line) self.__write_obj.write(line) def __section_func(self, line): - """ + ''' Requires: line --the line to parse Returns: nothing Logic: - """ + ''' if self.__token_info == 'cw<sc<sect-defin': self.__found_section_def_func(line) self.__write_obj.write(line) def __section_def_func(self, line): - """ + ''' Required: line --line to parse Returns: @@ -209,7 +209,7 @@ class Sections: the defnition (a paragraph definition), or if it contains info that should be added to the values dictionary. If neither of these cases are true, output the line to a file. - """ + ''' action, name = self.__section_def_dict.get(self.__token_info, (None, None)) if action: action(line, name) @@ -221,7 +221,7 @@ class Sections: self.__write_obj.write(line) def __end_sec_def_func(self, line, name): - """ + ''' Requires: line --the line to parse name --changed, readable name @@ -230,7 +230,7 @@ class Sections: Logic: The end of the section definition has been found. Reset the state. Call on the write_section method. - """ + ''' if not self.__in_field: self.__state = 'body' else: @@ -238,7 +238,7 @@ class Sections: self.__write_section(line) def __end_sec_premature_func(self, line, name): - """ + ''' Requires: line --the line to parse name --changed, readable name @@ -249,7 +249,7 @@ class Sections: before \\pard. This should indicate older RTF. Reset the state Write the section definition. Insert a paragraph definition. Insert {} to mark the end of a paragraph definition - """ + ''' if not self.__in_field: self.__state = 'body' else: @@ -260,7 +260,7 @@ class Sections: self.__write_obj.write('cb<nu<clos-brack<0000\n') def __write_section(self, line): - """ + ''' Requires: nothing Returns: @@ -269,7 +269,7 @@ class Sections: Form a string of attributes and values. If you are not in a field block, write this string to the output file. Otherwise, call on the handle_sec_def method to handle this string. - """ + ''' my_string = self.__mark_start if self.__found_first_sec: my_string += 'mi<tg<close_____<section\n' @@ -295,7 +295,7 @@ class Sections: raise self.__bug_handler(msg) def __handle_sec_def(self, my_string): - """ + ''' Requires: my_string -- the string of attributes and values. (Do I need this?) Returns: @@ -303,12 +303,12 @@ class Sections: Logic: I need to append the dictionary of attributes and values to list so I can use it later when I reach the end of the field-block. - """ + ''' values_dict = self.__section_values self.__list_of_sec_values.append(values_dict) def __body_func(self, line): - """ + ''' Requires: line --the line to parse Returns: @@ -316,7 +316,7 @@ class Sections: Logic: Look for the beginning of a section. Otherwise, print the line to the output file. - """ + ''' action = self.__body_dict.get(self.__token_info) if action: action(line) @@ -324,20 +324,20 @@ class Sections: self.__write_obj.write(line) def __before_body_func(self, line): - """ + ''' Requires: line --line to parse Returns: nothing Logic: Look for the beginning of the body. Always print out the line. - """ + ''' if self.__token_info == 'mi<mk<body-open_': self.__state = 'before_first_sec' self.__write_obj.write(line) def __before_first_sec_func(self, line): - """ + ''' Requires: line -- line to parse Returns: @@ -345,7 +345,7 @@ class Sections: Logic: Look for the beginning of the first section. This can be \\sectd, but in older RTF it could mean the any paragraph or row definition - """ + ''' if self.__token_info == 'cw<sc<sect-defin': self.__state = 'section_def' self.__section_num += 1 @@ -378,7 +378,7 @@ class Sections: self.__write_obj.write(line) def __found_sec_in_field_func(self, line): - """ + ''' Requires: line --line to parse Returns: @@ -387,13 +387,13 @@ class Sections: I have found the beginning of a field that has a section (or really, two) inside of it. Change the state, and start adding to one long string. - """ + ''' self.__state = 'sec_in_field' self.__sec_in_field_string = line self.__in_field = 1 def __sec_in_field_func(self, line): - """ + ''' Requires: line --the line to parse Returns: @@ -403,7 +403,7 @@ class Sections: definition. CHANGED! Just print out each line. Ignore any sections or section definition info. - """ + ''' action = self.__sec_in_field_dict.get(self.__token_info) if action: action(line) @@ -413,7 +413,7 @@ class Sections: self.__write_obj.write(line) def __end_sec_in_field_func(self, line): - """ + ''' Requires: line --line to parse Returns: @@ -424,15 +424,15 @@ class Sections: section tag. Print out the field string. Call on the same method to again write the close and beginning of a section tag. Change the state. - """ + ''' # change this 2004-04-26 # Don't do anything - """ + ''' self.__sec_in_field_string += line self.__print_field_sec_attributes() self.__write_obj.write(self.__sec_in_field_string) self.__print_field_sec_attributes() - """ + ''' self.__state = 'body' self.__in_field = 0 # this is changed too @@ -477,7 +477,7 @@ class Sections: # Look here def __found_section_in_field_func(self, line): - """ + ''' Requires: line --line to parse Returns: @@ -485,13 +485,13 @@ class Sections: Logic: I have found a section in a field block. Add one to section counter, and append this number to a list. - """ + ''' self.__section_num += 1 self.__field_num.append(self.__section_num) self.__sec_in_field_string += line def __found_section_def_in_field_func(self, line): - """ + ''' Requires: line --line to parse Returns: @@ -499,12 +499,12 @@ class Sections: Logic: I have found a section definition in a filed block. Change the state and clear the values dictionary. - """ + ''' self.__state = 'section_def' self.__section_values.clear() def make_sections(self): - """ + ''' Requires: nothing Returns: @@ -514,7 +514,7 @@ class Sections: the state. If the state is before the body, look for the beginning of the body. If the state is body, send the line to the body method. - """ + ''' self.__initiate_values() read_obj = open_for_read(self.__file) self.__write_obj = open_for_write(self.__write_to) @@ -532,6 +532,6 @@ class Sections: self.__write_obj.close() copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "sections.data") + copy_obj.copy_file(self.__write_to, 'sections.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/styles.py b/src/calibre/ebooks/rtf2xml/styles.py index 78aeabe195..275829552a 100644 --- a/src/calibre/ebooks/rtf2xml/styles.py +++ b/src/calibre/ebooks/rtf2xml/styles.py @@ -20,9 +20,9 @@ from . import open_for_read, open_for_write class Styles: - """ + ''' Change lines with style numbers to actual style names. - """ + ''' def __init__(self, in_file, @@ -47,9 +47,9 @@ class Styles: self.__run_level = run_level def __initiate_values(self): - """ + ''' Initiate all values. - """ + ''' self.__border_obj = border_parse.BorderParse() self.__styles_dict = {'par':{}, 'char':{}} self.__styles_num = '0' @@ -267,7 +267,7 @@ class Styles: self.__leader_found = 0 def __in_individual_style_func(self, line): - """ + ''' Required: line Returns: @@ -283,7 +283,7 @@ class Styles: Write an error message if no key is found for the info. If the line is text, add the text to a text string. The text string will be the name of the style. - """ + ''' action = self.__state_dict.get(self.__token_info) if action: action(line) @@ -342,8 +342,8 @@ class Styles: self.__leader_found = 0 def __tab_type_func(self, line): - """ - """ + ''' + ''' type = self.__tab_type_dict.get(self.__token_info) if type is not None: self.__tab_type = type @@ -402,7 +402,7 @@ class Styles: self.__tab_type = 'left' def __enter_dict_entry(self, att, value): - """ + ''' Required: att -- the attribute value -- the value @@ -412,14 +412,14 @@ class Styles: Try to add the attribute value directly to the styles dictionary. If a keyerror is found, that means I have to build the "branches" of the dictionary before I can add the key value pair. - """ + ''' try: self.__styles_dict[self.__type_of_style][self.__styles_num][att] = value except KeyError: self.__add_dict_entry(att, value) def __add_dict_entry(self, att, value): - """ + ''' Required: att --the attribute value --the value @@ -434,7 +434,7 @@ class Styles: Next, create a second, smaller dictionary with just the attribute and value. Add the small dictionary to the type dictionary. Add this type dictionary to the main styles dictionary. - """ + ''' if self.__type_of_style == 'par': type_dict =self.__styles_dict['par'] elif self.__type_of_style == 'char': @@ -449,7 +449,7 @@ class Styles: self.__styles_dict[self.__type_of_style] = type_dict def __para_style_func(self, line): - """ + ''' Required: line Returns: @@ -457,19 +457,19 @@ class Styles: Logic: Set the type of style to paragraph. Extract the number for a line such as "cw<ss<para-style<nu<15". - """ + ''' self.__type_of_style = 'par' self.__styles_num = line[20:-1] - """ + ''' self.__enter_dict_entry('tabs-left', '') self.__enter_dict_entry('tabs-right', '') self.__enter_dict_entry('tabs-center', '') self.__enter_dict_entry('tabs-decimal', '') self.__enter_dict_entry('tabs-bar', '') - """ + ''' def __char_style_func(self, line): - """ + ''' Required: line Returns: @@ -477,7 +477,7 @@ class Styles: Logic: Set the type of style to character. Extract the number for a line such as "cw<ss<char-style<nu<15". - """ + ''' self.__type_of_style = 'char' self.__styles_num = line[20:-1] @@ -503,7 +503,7 @@ class Styles: self.__text_string = '' def __found_end_styles_table_func(self, line): - """ + ''' Required: line Returns: @@ -512,7 +512,7 @@ class Styles: Set the state to after the styles table. Fix the styles. (I explain this below.) Print out the style table. - """ + ''' self.__state = 'after_styles_table' self.__fix_based_on() self.__print_style_table() @@ -558,7 +558,7 @@ class Styles: del self.__styles_dict[type][key][style] def __print_style_table(self): - """ + ''' Required: nothing Returns: @@ -570,7 +570,7 @@ class Styles: The next loop iterates through the style numbers. The most inside loop iterates over the pairs of attributes and values, and prints them out. - """ + ''' types = ['par', 'char'] for type in types: if type == 'par': @@ -597,18 +597,18 @@ class Styles: ) def __found_styles_table_func(self, line): - """ + ''' Required: line Returns: nothing Logic: Change the state to in the style table when the marker has been found. - """ + ''' self.__state = 'in_styles_table' def __before_styles_func(self, line): - """ + ''' Required: line Returns: @@ -617,7 +617,7 @@ class Styles: Check the line info in the state dictionary. When the beginning of the styles table is found, change the state to in the styles table. - """ + ''' action = self.__state_dict.get(self.__token_info) if not action: self.__write_obj.write(line) @@ -625,7 +625,7 @@ class Styles: action(line) def __in_styles_func(self, line): - """ + ''' Required: line Returns: @@ -633,7 +633,7 @@ class Styles: Logic: Check the line for the beginning of an individual style. If it is not found, simply print out the line. - """ + ''' action = self.__state_dict.get(self.__token_info) if action is None: self.__write_obj.write(line) @@ -641,7 +641,7 @@ class Styles: action(line) def __para_style_in_body_func(self, line, type): - """ + ''' Required: line-- the line type -- whether a character or paragraph @@ -651,7 +651,7 @@ class Styles: Determine the prefix by whether the type is "par" or "char". Extract the number from a line such as "cw<ss<para-style<nu<15". Look up that number in the styles dictionary and put a name for a number - """ + ''' if type == 'par': prefix = 'para' else: @@ -672,7 +672,7 @@ class Styles: ) def __after_styles_func(self, line): - """ + ''' Required: line Returns: @@ -681,7 +681,7 @@ class Styles: Determine if a line with either character of paragraph style info has been found. If so, then use the appropriate method to parse the line. Otherwise, write the line to a file. - """ + ''' action, type = self.__body_dict.get(self.__token_info, (None, None)) if action: action(line, type) @@ -689,7 +689,7 @@ class Styles: self.__write_obj.write(line) def convert_styles(self): - """ + ''' Requires: nothing Returns: @@ -702,7 +702,7 @@ class Styles: and print out the tags. If the state if after the style table, look for lines with style info, and substitute the number with the name of the style. - """ + ''' self.__initiate_values() read_obj = open_for_read(self.__file) self.__write_obj = open_for_write(self.__write_to) @@ -720,6 +720,6 @@ class Styles: self.__write_obj.close() copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "styles.data") + copy_obj.copy_file(self.__write_to, 'styles.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/table.py b/src/calibre/ebooks/rtf2xml/table.py index 774fcee278..0b2feced3f 100644 --- a/src/calibre/ebooks/rtf2xml/table.py +++ b/src/calibre/ebooks/rtf2xml/table.py @@ -18,7 +18,7 @@ from calibre.ptempfile import better_mktemp from . import open_for_read, open_for_write -""" +''' States. 1. 'not_in_table' 1. 'cw<tb<row-def___' start a row definition @@ -43,7 +43,7 @@ States. 5. 'in_cell' 1. 'mi<mk<not-in-tbl', end table 2. 'cw<tb<cell______', end cell -""" +''' class Table: @@ -76,9 +76,9 @@ class Table: self.__write_to = better_mktemp() def __initiate_values(self): - """ + ''' Initiate all values. - """ + ''' self.__state_dict = { 'in_table': self.__in_table_func, 'in_row_def': self.__in_row_def_func, @@ -212,14 +212,14 @@ class Table: self.__state.append('in_table') def __end_row_table_func(self, line): - """ + ''' Requires: line --just for consistencey Returns: ? Logic: ? - """ + ''' self.__close_table(self, line) def __end_row_def_func(self, line): @@ -246,7 +246,7 @@ class Table: self.__row_dict['number-of-cells'] = num_cells def __in_row_def_func(self, line): - """ + ''' Requires: line --line to parse Returns: @@ -258,7 +258,7 @@ class Table: While in the row definition, certain tokens can end a row or end a table. If a paragrah definition (pard-start) is found, and the you are already in a table, start of a row. - """ + ''' if self.__token_info == 'cw<tb<row_______': # write tags self.__end_row_func(line) @@ -289,7 +289,7 @@ class Table: self.__write_obj.write(line) def __handle_row_token(self, line): - """ + ''' Requires: line -- line to parse Returns: @@ -304,7 +304,7 @@ class Table: the last item in the cell list. ([{border:something, width:something}, {border:something, width:something}]) cw<bd<bor-t-r-to<nu<bdr-hair__|bdr-li-wid:0.50 - """ + ''' if line[3:5] == 'bd': border_obj = border_parse.BorderParse() the_dict = border_obj.parse_border(line) @@ -380,7 +380,7 @@ class Table: self.__rows_in_table += 1 def __found_cell_position(self, line): - """ + ''' needs: line: current line returns: @@ -390,7 +390,7 @@ class Table: If the cell is the first cell, you should add the left cell position to it. (This value is often negative.) Next, set the new last_cell_position to the current cell position. - """ + ''' # cw<tb<cell-posit<nu<216.00 new_cell_position = round(float(line[20:-1]), 2) left_position = 0 @@ -411,7 +411,7 @@ class Table: self.__cell_widths.append(width) def __in_cell_func(self, line): - """ + ''' Required: line Returns: @@ -423,7 +423,7 @@ class Table: Look for the close of the cell. If found, use the close cell function to close out the cell. Otherwise, print out the line. - """ + ''' # cw<tb<cell______<nu<true # mi<mk<sect-start if self.__token_info == 'mi<mk<not-in-tbl' or\ @@ -440,14 +440,14 @@ class Table: self.__write_obj.write(line) def __end_cell_func(self, line): - """ + ''' Requires: line Returns: nothing Logic: End the cell. Print out the closing marks. Pop the self.__state. - """ + ''' if len(self.__state) > 1: if self.__state[-1] == 'in_cell': self.__state.pop() @@ -468,7 +468,7 @@ class Table: if action: action(line) self.__write_obj.write(line) - """ + ''' elif self.__token_info == 'mi<mk<pard-start': self.__start_cell_func(line) self.__write_obj.write(line) @@ -477,11 +477,11 @@ class Table: self.__write_obj.write(line) else: self.__write_obj.write(line) - """ + ''' def __end_row_func(self, line): - """ - """ + ''' + ''' if len(self.__state) > 1 and self.__state[-1] == 'in_row': self.__state.pop() self.__write_obj.write('mi<tg<close_____<row\n') @@ -493,7 +493,7 @@ class Table: self.__list_of_cells_in_row.append(self.__cells_in_row) def __empty_cell(self, line): - """ + ''' Required: line -- line of text Returns: @@ -501,7 +501,7 @@ class Table: Logic: Write an empty tag with attributes if there are attributes. Otherwise, written an empty tag with cell as element. - """ + ''' if len(self.__cell_list) > 0: self.__write_obj.write('mi<tg<empty-att_<cell') cell_dict = self.__cell_list[-1] @@ -515,7 +515,7 @@ class Table: self.__cells_in_row += 1 def __mode(self, the_list): - """ + ''' Required: the_list -- a list of something Returns: @@ -523,7 +523,7 @@ class Table: Logic: get the count of each item in list. The count that is the greatest is the mode. - """ + ''' max = 0 mode = 'not-defined' for item in the_list: @@ -534,7 +534,7 @@ class Table: return mode def make_table(self): - """ + ''' Requires: nothing Returns: @@ -542,7 +542,7 @@ class Table: Logic: Read one line in at a time. Determine what action to take based on the state. - """ + ''' self.__initiate_values() read_obj = open_for_read(self.__file) self.__write_obj = open_for_write(self.__write_to) @@ -561,7 +561,7 @@ class Table: self.__write_obj.close() copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "table.data") + copy_obj.copy_file(self.__write_to, 'table.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) return self.__table_data diff --git a/src/calibre/ebooks/rtf2xml/table_info.py b/src/calibre/ebooks/rtf2xml/table_info.py index 56983c2280..af4fcc0cde 100644 --- a/src/calibre/ebooks/rtf2xml/table_info.py +++ b/src/calibre/ebooks/rtf2xml/table_info.py @@ -18,15 +18,15 @@ from calibre.ptempfile import better_mktemp from . import open_for_read, open_for_write # note to self. This is the first module in which I use tempfile. A good idea? -""" -""" +''' +''' class TableInfo: - """ + ''' Insert table data for tables. Logic: - """ + ''' def __init__(self, in_file, @@ -54,8 +54,8 @@ class TableInfo: # self.__write_to = 'table_info.data' def insert_info(self): - """ - """ + ''' + ''' read_obj = open_for_read(self.__file) self.__write_obj = open_for_write(self.__write_to) line_to_read = 1 @@ -84,6 +84,6 @@ class TableInfo: self.__write_obj.close() copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "table_info.data") + copy_obj.copy_file(self.__write_to, 'table_info.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/rtf2xml/tokenize.py b/src/calibre/ebooks/rtf2xml/tokenize.py index 6a9bde8830..fbdd1ebad3 100644 --- a/src/calibre/ebooks/rtf2xml/tokenize.py +++ b/src/calibre/ebooks/rtf2xml/tokenize.py @@ -22,7 +22,7 @@ from . import open_for_read, open_for_write class Tokenize: - """Tokenize RTF into one line per field. Each line will contain information useful for the rest of the script""" + '''Tokenize RTF into one line per field. Each line will contain information useful for the rest of the script''' def __init__(self, in_file, @@ -84,7 +84,7 @@ class Tokenize: self.__uc_char -=1 self.__uc_bin = True return '' - elif token[:1] == "\\" : + elif token[:1] == '\\' : self.__uc_char -=1 return '' else: @@ -118,10 +118,10 @@ class Tokenize: input_file = self.__replace_spchar.mreplace(input_file) # this is for older RTF input_file = self.__par_exp.sub(r'\n\\par \n', input_file) - input_file = self.__cwdigit_exp.sub(r"\g<1>\n\g<2>", input_file) - input_file = self.__cs_ast.sub(r"\g<1>", input_file) - input_file = self.__ms_hex_exp.sub(r"\\mshex0\g<1> ", input_file) - input_file = self.__utf_ud.sub(r"\\{\\uc0 \g<1>\\}", input_file) + input_file = self.__cwdigit_exp.sub(r'\g<1>\n\g<2>', input_file) + input_file = self.__cs_ast.sub(r'\g<1>', input_file) + input_file = self.__ms_hex_exp.sub(r'\\mshex0\g<1> ', input_file) + input_file = self.__utf_ud.sub(r'\\{\\uc0 \g<1>\\}', input_file) # remove \n in bin data input_file = self.__bin_exp.sub(lambda x: x.group().replace('\n', '') + '\n', input_file) @@ -132,51 +132,51 @@ class Tokenize: def __compile_expressions(self): SIMPLE_RPL = { - "\\\\": "\\backslash ", - "\\~": "\\~ ", - "\\;": "\\; ", - "&": "&", - "<": "<", - ">": ">", - "\\_": "\\_ ", - "\\:": "\\: ", - "\\-": "\\- ", + '\\\\': '\\backslash ', + '\\~': '\\~ ', + '\\;': '\\; ', + '&': '&', + '<': '<', + '>': '>', + '\\_': '\\_ ', + '\\:': '\\: ', + '\\-': '\\- ', # turn into a generic token to eliminate special # cases and make processing easier - "\\{": "\\ob ", + '\\{': '\\ob ', # turn into a generic token to eliminate special # cases and make processing easier - "\\}": "\\cb ", + '\\}': '\\cb ', # put a backslash in front of to eliminate special cases and # make processing easier - "{": "\\{", + '{': '\\{', # put a backslash in front of to eliminate special cases and # make processing easier - "}": "\\}", + '}': '\\}', } self.__replace_spchar = MReplace(SIMPLE_RPL) # add ;? in case of char following \u self.__ms_hex_exp = re.compile(r"\\\'([0-9a-fA-F]{2})") - self.__utf_exp = re.compile(r"\\u(-?\d{3,6}) ?") - self.__bin_exp = re.compile(r"(?:\\bin(-?\d{0,10})[\n ]+)[01\n]+") + self.__utf_exp = re.compile(r'\\u(-?\d{3,6}) ?') + self.__bin_exp = re.compile(r'(?:\\bin(-?\d{0,10})[\n ]+)[01\n]+') # manage upr/ud situations - self.__utf_ud = re.compile(r"\\{[\n ]?\\upr[\n ]?(?:\\{.*?\\})[\n ]?" + - r"\\{[\n ]?\\*[\n ]?\\ud[\n ]?(\\{.*?\\})[\n ]?\\}[\n ]?\\}") + self.__utf_ud = re.compile(r'\\{[\n ]?\\upr[\n ]?(?:\\{.*?\\})[\n ]?' + + r'\\{[\n ]?\\*[\n ]?\\ud[\n ]?(\\{.*?\\})[\n ]?\\}[\n ]?\\}') # add \n in split for whole file reading # why keep backslash whereas \is replaced before? # remove \n from endline char - self.__splitexp = re.compile(r"(\\[{}]|\n|\\[^\s\\{}&]+(?:[ \t\r\f\v])?)") + self.__splitexp = re.compile(r'(\\[{}]|\n|\\[^\s\\{}&]+(?:[ \t\r\f\v])?)') # this is for old RTF self.__par_exp = re.compile(r'(\\\n+|\\ )') # handle improper cs char-style with \* before without { self.__cs_ast = re.compile(r'\\\*([\n ]*\\cs\d+[\n \\]+)') # handle cw using a digit as argument and without space as delimiter - self.__cwdigit_exp = re.compile(r"(\\[a-zA-Z]+[\-0-9]+)([^0-9 \\]+)") + self.__cwdigit_exp = re.compile(r'(\\[a-zA-Z]+[\-0-9]+)([^0-9 \\]+)') def tokenize(self): - """Main class for handling other methods. Reads the file \ + '''Main class for handling other methods. Reads the file \ , uses method self.sub_reg to make basic substitutions,\ - and process tokens by itself""" + and process tokens by itself''' # read with open_for_read(self.__file) as read_obj: input_file = read_obj.read() @@ -195,7 +195,7 @@ class Tokenize: # Move and copy copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: - copy_obj.copy_file(self.__write_to, "tokenize.data") + copy_obj.copy_file(self.__write_to, 'tokenize.data') copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to) diff --git a/src/calibre/ebooks/snb/snbfile.py b/src/calibre/ebooks/snb/snbfile.py index 07536bf880..6044728894 100644 --- a/src/calibre/ebooks/snb/snbfile.py +++ b/src/calibre/ebooks/snb/snbfile.py @@ -39,7 +39,7 @@ class SNBFile: def Open(self, inputFile): self.fileName = inputFile - with open(self.fileName, "rb") as f: + with open(self.fileName, 'rb') as f: f.seek(0) self.Parse(f) @@ -74,7 +74,7 @@ class SNBFile: if f.attr & 0x41000000 == 0x41000000: # Compressed Files if uncompressedData is None: - uncompressedData = b"" + uncompressedData = b'' for i in range(self.plainBlock): bzdc = bz2.BZ2Decompressor() if (i < self.plainBlock - 1): @@ -101,7 +101,7 @@ class SNBFile: f.fileBody = snbFile.read(f.fileSize) binPos += f.fileSize else: - raise ValueError(f"Invalid file: {f.attr} {f.fileName}") + raise ValueError(f'Invalid file: {f.attr} {f.fileName}') def ParseFile(self, vfat, fileCount): fileNames = vfat[fileCount*12:].split(b'\0') @@ -148,7 +148,7 @@ class SNBFile: for root, dirs, files in os.walk(tdir): for name in files: p, ext = os.path.splitext(name) - if ext in [".snbf", ".snbc"]: + if ext in ['.snbf', '.snbc']: self.AppendPlain(os.path.relpath(os.path.join(root, name), tdir), tdir) else: self.AppendBinary(os.path.relpath(os.path.join(root, name), tdir), tdir) @@ -161,7 +161,7 @@ class SNBFile: f.fileBody = data.read() f.fileName = fileName.replace(os.sep, '/') if isinstance(f.fileName, str): - f.fileName = f.fileName.encode("ascii", "ignore") + f.fileName = f.fileName.encode('ascii', 'ignore') self.files.append(f) def AppendBinary(self, fileName, tdir): @@ -172,7 +172,7 @@ class SNBFile: f.fileBody = data.read() f.fileName = fileName.replace(os.sep, '/') if isinstance(f.fileName, str): - f.fileName = f.fileName.encode("ascii", "ignore") + f.fileName = f.fileName.encode('ascii', 'ignore') self.files.append(f) def GetFileStream(self, fileName): @@ -220,7 +220,7 @@ class SNBFile: f.contentOffset = len(binStream) binStream += f.fileBody else: - raise Exception(f"Unknown file type: {f.attr} {f.fileName}") + raise Exception(f'Unknown file type: {f.attr} {f.fileName}') vfatCompressed = zlib.compress(vfat+fileNameTable) # File header part 2 @@ -282,31 +282,31 @@ class SNBFile: def Dump(self): if self.fileName: - print("File Name:\t", self.fileName) - print("File Count:\t", self.fileCount) - print("VFAT Size(Compressed):\t%d(%d)" % (self.vfatSize, self.vfatCompressed)) - print("Binary Stream Size:\t", self.binStreamSize) - print("Plain Stream Uncompressed Size:\t", self.plainStreamSizeUncompressed) - print("Binary Block Count:\t", self.binBlock) - print("Plain Block Count:\t", self.plainBlock) + print('File Name:\t', self.fileName) + print('File Count:\t', self.fileCount) + print('VFAT Size(Compressed):\t%d(%d)' % (self.vfatSize, self.vfatCompressed)) + print('Binary Stream Size:\t', self.binStreamSize) + print('Plain Stream Uncompressed Size:\t', self.plainStreamSizeUncompressed) + print('Binary Block Count:\t', self.binBlock) + print('Plain Block Count:\t', self.plainBlock) for i in range(self.fileCount): - print("File ", i) + print('File ', i) f = self.files[i] - print("File Name: ", f.fileName) - print("File Attr: ", f.attr) - print("File Size: ", f.fileSize) - print("Block Index: ", f.blockIndex) - print("Content Offset: ", f.contentOffset) - with open("/tmp/" + f.fileName, 'wb') as tempFile: + print('File Name: ', f.fileName) + print('File Attr: ', f.attr) + print('File Size: ', f.fileSize) + print('Block Index: ', f.blockIndex) + print('Content Offset: ', f.contentOffset) + with open('/tmp/' + f.fileName, 'wb') as tempFile: tempFile.write(f.fileBody) def usage(): - print("This unit test is for INTERNAL usage only!") - print("This unit test accept two parameters.") - print("python snbfile.py <INPUTFILE> <DESTFILE>") - print("The input file will be extracted and write to dest file. ") - print("Meta data of the file will be shown during this process.") + print('This unit test is for INTERNAL usage only!') + print('This unit test accept two parameters.') + print('python snbfile.py <INPUTFILE> <DESTFILE>') + print('The input file will be extracted and write to dest file. ') + print('Meta data of the file will be shown during this process.') def main(): @@ -316,19 +316,19 @@ def main(): inputFile = sys.argv[1] outputFile = sys.argv[2] - print("Input file: ", inputFile) - print("Output file: ", outputFile) + print('Input file: ', inputFile) + print('Output file: ', outputFile) snbFile = SNBFile(inputFile) if snbFile.IsValid(): snbFile.Dump() snbFile.Output(outputFile) else: - print("The input file is invalid.") + print('The input file is invalid.') return 1 return 0 -if __name__ == "__main__": - """SNB file unit test""" +if __name__ == '__main__': + '''SNB file unit test''' sys.exit(main()) diff --git a/src/calibre/ebooks/snb/snbml.py b/src/calibre/ebooks/snb/snbml.py index 4d09d20cb6..4cd82498da 100644 --- a/src/calibre/ebooks/snb/snbml.py +++ b/src/calibre/ebooks/snb/snbml.py @@ -16,9 +16,9 @@ from polyglot.builtins import string_or_bytes def ProcessFileName(fileName): # Flat the path - fileName = fileName.replace("/", "_").replace(os.sep, "_") + fileName = fileName.replace('/', '_').replace(os.sep, '_') # Handle bookmark for HTML file - fileName = fileName.replace("#", "_") + fileName = fileName.replace('#', '_') # Make it lower case fileName = fileName.lower() # Change all images to jpg @@ -49,14 +49,14 @@ SPACE_TAGS = [ 'td', ] -CALIBRE_SNB_IMG_TAG = "<$$calibre_snb_temp_img$$>" -CALIBRE_SNB_BM_TAG = "<$$calibre_snb_bm_tag$$>" -CALIBRE_SNB_PRE_TAG = "<$$calibre_snb_pre_tag$$>" +CALIBRE_SNB_IMG_TAG = '<$$calibre_snb_temp_img$$>' +CALIBRE_SNB_BM_TAG = '<$$calibre_snb_bm_tag$$>' +CALIBRE_SNB_PRE_TAG = '<$$calibre_snb_pre_tag$$>' class SNBMLizer: - curSubItem = "" + curSubItem = '' # curText = [ ] def __init__(self, log): @@ -72,10 +72,10 @@ class SNBMLizer: def merge_content(self, old_tree, oeb_book, item, subitems, opts): newTrees = self.extract_content(oeb_book, item, subitems, opts) - body = old_tree.find(".//body") + body = old_tree.find('.//body') if body is not None: for subName in newTrees: - newbody = newTrees[subName].find(".//body") + newbody = newTrees[subName].find('.//body') for entity in newbody: body.append(entity) @@ -89,48 +89,48 @@ class SNBMLizer: # content = self.remove_newlines(content) trees = {} for subitem, subtitle in self.subitems: - snbcTree = etree.Element("snbc") - snbcHead = etree.SubElement(snbcTree, "head") - etree.SubElement(snbcHead, "title").text = subtitle + snbcTree = etree.Element('snbc') + snbcHead = etree.SubElement(snbcTree, 'head') + etree.SubElement(snbcHead, 'title').text = subtitle if self.opts and self.opts.snb_hide_chapter_name: - etree.SubElement(snbcHead, "hidetitle").text = "true" - etree.SubElement(snbcTree, "body") + etree.SubElement(snbcHead, 'hidetitle').text = 'true' + etree.SubElement(snbcTree, 'body') trees[subitem] = snbcTree - output.append('{}{}\n\n'.format(CALIBRE_SNB_BM_TAG, "")) + output.append('{}{}\n\n'.format(CALIBRE_SNB_BM_TAG, '')) output += self.dump_text(self.subitems, safe_xml_fromstring(content), stylizer)[0] output = self.cleanup_text(''.join(output)) subitem = '' - bodyTree = trees[subitem].find(".//body") + bodyTree = trees[subitem].find('.//body') for line in output.splitlines(): pos = line.find(CALIBRE_SNB_PRE_TAG) if pos == -1: line = line.strip(' \t\n\r\u3000') else: - etree.SubElement(bodyTree, "text").text = \ + etree.SubElement(bodyTree, 'text').text = \ etree.CDATA(line[pos+len(CALIBRE_SNB_PRE_TAG):]) continue if len(line) != 0: if line.find(CALIBRE_SNB_IMG_TAG) == 0: prefix = ProcessFileName(os.path.dirname(self.item.href)) if prefix != '': - etree.SubElement(bodyTree, "img").text = \ + etree.SubElement(bodyTree, 'img').text = \ prefix + '_' + line[len(CALIBRE_SNB_IMG_TAG):] else: - etree.SubElement(bodyTree, "img").text = \ + etree.SubElement(bodyTree, 'img').text = \ line[len(CALIBRE_SNB_IMG_TAG):] elif line.find(CALIBRE_SNB_BM_TAG) == 0: subitem = line[len(CALIBRE_SNB_BM_TAG):] - bodyTree = trees[subitem].find(".//body") + bodyTree = trees[subitem].find('.//body') else: if self.opts and not self.opts.snb_dont_indent_first_line: prefix = '\u3000\u3000' else: prefix = '' - etree.SubElement(bodyTree, "text").text = \ + etree.SubElement(bodyTree, 'text').text = \ etree.CDATA(str(prefix + line)) if self.opts and self.opts.snb_insert_empty_line: - etree.SubElement(bodyTree, "text").text = \ + etree.SubElement(bodyTree, 'text').text = \ etree.CDATA('') return trees diff --git a/src/calibre/ebooks/textile/functions.py b/src/calibre/ebooks/textile/functions.py index 73389dd071..216509d371 100644 --- a/src/calibre/ebooks/textile/functions.py +++ b/src/calibre/ebooks/textile/functions.py @@ -1,17 +1,17 @@ #!/usr/bin/env python -""" +''' PyTextile A Humane Web Text Generator -""" +''' # Last upstream version basis # __version__ = '2.1.4' # __date__ = '2009/12/04' -__copyright__ = """ +__copyright__ = ''' Copyright (c) 2011, Leigh Parry <leighparry@blueyonder.co.uk> Copyright (c) 2011, John Schember <john@nachtimwald.com> Copyright (c) 2009, Jason Samsa, http://jsamsa.com/ @@ -27,9 +27,9 @@ Textile's procedural code into a class framework Additions and fixes Copyright (c) 2006 Alex Shiels http://thresholdstate.com/ -""" +''' -__license__ = """ +__license__ = ''' L I C E N S E ============= Redistribution and use in source and binary forms, with or without @@ -58,7 +58,7 @@ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" +''' import re import uuid @@ -262,7 +262,7 @@ class Textile: ] def __init__(self, restricted=False, lite=False, noimage=False): - """docstring for __init__""" + '''docstring for __init__''' self.restricted = restricted self.lite = lite self.noimage = noimage @@ -362,7 +362,7 @@ class Textile: if element == 'td' or element == 'tr': m = re.search(r'(%s)' % self.vlgn, matched) if m: - style.append("vertical-align:%s;" % self.vAlign(m.group(1))) + style.append('vertical-align:%s;' % self.vAlign(m.group(1))) m = re.search(r'\{([^}]*)\}', matched) if m: @@ -381,17 +381,17 @@ class Textile: m = re.search(r'([(]+)', matched) if m: - style.append("padding-left:%sem;" % len(m.group(1))) + style.append('padding-left:%sem;' % len(m.group(1))) matched = matched.replace(m.group(0), '') m = re.search(r'([)]+)', matched) if m: - style.append("padding-right:%sem;" % len(m.group(1))) + style.append('padding-right:%sem;' % len(m.group(1))) matched = matched.replace(m.group(0), '') m = re.search(r'(%s)' % self.hlgn, matched) if m: - style.append("text-align:%s;" % self.hAlign(m.group(1))) + style.append('text-align:%s;' % self.hAlign(m.group(1))) m = re.search(r'^(.*)#(.*)$', aclass) if m: @@ -406,7 +406,7 @@ class Textile: result = [] if style: - result.append(' style="%s"' % "".join(style)) + result.append(' style="%s"' % ''.join(style)) if aclass: result.append(' class="%s"' % aclass) if lang: @@ -441,7 +441,7 @@ class Textile: >>> t.table('|one|two|three|\n|a|b|c|') '\t<table>\n\t\t<tr>\n\t\t\t<td>one</td>\n\t\t\t<td>two</td>\n\t\t\t<td>three</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>a</td>\n\t\t\t<td>b</td>\n\t\t\t<td>c</td>\n\t\t</tr>\n\t</table>\n\n' """ - text = text + "\n\n" + text = text + '\n\n' pattern = re.compile(r'^(?:table(_?%(s)s%(a)s%(c)s)\. ?\n)?^(%(a)s%(c)s\.? ?\|.*\|)\n\n' % {'s':self.s, 'a':self.a, 'c':self.c}, re.S|re.M|re.U) return pattern.sub(self.fTable, text) @@ -460,7 +460,7 @@ class Textile: for cell in row.split('|')[1:-1]: ctyp = 'd' if re.search(r'^_', cell): - ctyp = "h" + ctyp = 'h' cmtch = re.search(fr'^(_?{self.s}{self.a}{self.c}\. )(.*)', cell) if cmtch: catts = self.pba(cmtch.group(1), 'td') @@ -470,10 +470,10 @@ class Textile: cell = self.graf(self.span(cell)) cells.append(f'\t\t\t<t{ctyp}{catts}>{cell}</t{ctyp}>') - rows.append("\t\t<tr{}>\n{}\n\t\t</tr>".format(ratts, '\n'.join(cells))) + rows.append('\t\t<tr{}>\n{}\n\t\t</tr>'.format(ratts, '\n'.join(cells))) cells = [] catts = None - return "\t<table{}>\n{}\n\t</table>\n\n".format(tatts, '\n'.join(rows)) + return '\t<table{}>\n{}\n\t</table>\n\n'.format(tatts, '\n'.join(rows)) def lists(self, text): """ @@ -485,7 +485,7 @@ class Textile: return pattern.sub(self.fList, text) def fList(self, match): - text = match.group(0).split("\n") + text = match.group(0).split('\n') result = [] lists = [] for i, line in enumerate(text): @@ -494,7 +494,7 @@ class Textile: except IndexError: nextline = '' - m = re.search(fr"^([#*]+)({self.a}{self.c}) (.*)$", line, re.S) + m = re.search(fr'^([#*]+)({self.a}{self.c}) (.*)$', line, re.S) if m: tl, atts, content = m.groups() nl = '' @@ -504,21 +504,21 @@ class Textile: if tl not in lists: lists.append(tl) atts = self.pba(atts) - line = f"\t<{self.lT(tl)}l{atts}>\n\t\t<li>{self.graf(content)}" + line = f'\t<{self.lT(tl)}l{atts}>\n\t\t<li>{self.graf(content)}' else: - line = "\t\t<li>" + self.graf(content) + line = '\t\t<li>' + self.graf(content) if len(nl) <= len(tl): - line = line + "</li>" + line = line + '</li>' for k in reversed(lists): if len(k) > len(nl): - line = line + "\n\t</%sl>" % self.lT(k) + line = line + '\n\t</%sl>' % self.lT(k) if len(k) > 1: - line = line + "</li>" + line = line + '</li>' lists.remove(k) result.append(line) - return "\n".join(result) + return '\n'.join(result) def lT(self, input): if re.search(r'^#+', input): @@ -574,9 +574,9 @@ class Textile: # we'll close it at the start of the next block if ext: - line = f"{o1}{o2}{content}{c2}" + line = f'{o1}{o2}{content}{c2}' else: - line = f"{o1}{o2}{content}{c2}{c1}" + line = f'{o1}{o2}{content}{c2}{c1}' else: anon = True @@ -588,7 +588,7 @@ class Textile: if tag == 'p' and not self.hasRawText(content): line = content else: - line = f"{o2}{content}{c2}" + line = f'{o2}{content}{c2}' else: line = self.graf(line) @@ -597,7 +597,7 @@ class Textile: line = re.sub(r'<br>', '<br />', line) if ext and anon: - out.append(out.pop() + "\n" + line) + out.append(out.pop() + '\n' + line) else: out.append(line) @@ -647,17 +647,17 @@ class Textile: cite = ' cite="%s"' % cite else: cite = '' - o1 = f"\t<blockquote{cite}{atts}>\n" - o2 = "\t\t<p%s>" % atts - c2 = "</p>" - c1 = "\n\t</blockquote>" + o1 = f'\t<blockquote{cite}{atts}>\n' + o2 = '\t\t<p%s>' % atts + c2 = '</p>' + c1 = '\n\t</blockquote>' elif tag == 'bc': - o1 = "<pre%s>" % atts - o2 = "<code%s>" % atts - c2 = "</code>" - c1 = "</pre>" - content = self.shelve(self.encode_html(content.rstrip("\n") + "\n")) + o1 = '<pre%s>' % atts + o2 = '<code%s>' % atts + c2 = '</code>' + c1 = '</pre>' + content = self.shelve(self.encode_html(content.rstrip('\n') + '\n')) elif tag == 'notextile': content = self.shelve(content) @@ -665,14 +665,14 @@ class Textile: c1 = c2 = '' elif tag == 'pre': - content = self.shelve(self.encode_html(content.rstrip("\n") + "\n")) - o1 = "<pre%s>" % atts + content = self.shelve(self.encode_html(content.rstrip('\n') + '\n')) + o1 = '<pre%s>' % atts o2 = c2 = '' c1 = '</pre>' else: - o2 = f"\t<{tag}{atts}>" - c2 = "</%s>" % tag + o2 = f'\t<{tag}{atts}>' + c2 = '</%s>' % tag content = self.graf(content) return o1, o2, content, c2, c1 @@ -718,7 +718,7 @@ class Textile: """ # fix: hackish - text = re.sub(r'"\Z', '\" ', text) + text = re.sub(r'"\Z', '" ', text) result = [] for line in re.compile(r'(<.*?>)', re.U).split(text): @@ -735,7 +735,7 @@ class Textile: def macros_only(self, text): # fix: hackish - text = re.sub(r'"\Z', '\" ', text) + text = re.sub(r'"\Z', '" ', text) result = [] for line in re.compile(r'(<.*?>)', re.U).split(text): @@ -757,9 +757,9 @@ class Textile: return d.get(input, '') def getRefs(self, text): - """ + ''' what is this for? - """ + ''' pattern = re.compile(r'(?:(?<=^)|(?<=\s))\[(.+)\]((?:http(?:s?):\/\/|\/)\S+)(?=\s|$)', re.U) text = pattern.sub(self.refs, text) return text @@ -773,7 +773,7 @@ class Textile: return self.urlrefs.get(url, url) def isRelURL(self, url): - """ + ''' Identify relative urls. >>> t = Textile() @@ -782,7 +782,7 @@ class Textile: >>> t.isRelURL("/foo") True - """ + ''' (scheme, netloc) = urlparse(url)[0:2] return not scheme and not netloc @@ -915,7 +915,7 @@ class Textile: pnct = ".,\"'?!;:" for qtag in qtags: - pattern = re.compile(r""" + pattern = re.compile(r''' (?:^|(?<=[\s>%(pnct)s\(])|\[|([\]}])) (%(qtag)s)(?!%(qtag)s) (%(c)s) @@ -924,7 +924,7 @@ class Textile: ([%(pnct)s]*) %(qtag)s (?:$|([\]}])|(?=%(selfpnct)s{1,2}|\s)) - """ % {'qtag':qtag, 'c':self.c, 'pnct':pnct, + ''' % {'qtag':qtag, 'c':self.c, 'pnct':pnct, 'selfpnct':self.pnct}, re.X) text = pattern.sub(self.fSpan, text) return text @@ -951,7 +951,7 @@ class Textile: content = self.span(content) - out = f"<{tag}{atts}>{content}{end}</{tag}>" + out = f'<{tag}{atts}>{content}{end}</{tag}>' return out def image(self, text): @@ -960,7 +960,7 @@ class Textile: >>> t.image('!/imgs/myphoto.jpg!:http://jsamsa.com') '<a href="http://jsamsa.com"><img src="/imgs/myphoto.jpg" alt="" /></a>' """ - pattern = re.compile(r""" + pattern = re.compile(r''' (?:[\[{])? # pre \! # opening ! (%s) # optional style,class atts @@ -971,7 +971,7 @@ class Textile: \! # closing (?::(\S+))? # optional href (?:[\]}]|(?=\s|$)) # lookahead: space or end of string - """ % self.c, re.U|re.X) + ''' % self.c, re.U|re.X) return pattern.sub(self.fImage, text) def fImage(self, match): @@ -987,7 +987,7 @@ class Textile: if not self.isRelURL(url) and self.get_sizes: size = getimagesize(url) if (size): - atts += " %s" % size + atts += ' %s' % size if href: href = self.checkRefs(href) @@ -1038,9 +1038,9 @@ class Textile: return pattern.sub(method, text) def fSpecial(self, match): - """ + ''' special blocks like notextile or code - """ + ''' before, text, after = match.groups() if after is None: after = '' diff --git a/src/calibre/ebooks/textile/unsmarten.py b/src/calibre/ebooks/textile/unsmarten.py index 588854aeb7..d7f91a4415 100644 --- a/src/calibre/ebooks/textile/unsmarten.py +++ b/src/calibre/ebooks/textile/unsmarten.py @@ -39,7 +39,7 @@ def unsmarten(txt): txt = re.sub('Ö|Ö|Ö', r'{O"}', txt) # O-umlaut txt = re.sub('×|×|×', r'{x}', txt) # dimension txt = re.sub('Ø|Ø|Ø', r'{O/}', txt) # O-slash - txt = re.sub('Ù|Ù|Ù', r"{U`}", txt) # U-grave + txt = re.sub('Ù|Ù|Ù', r'{U`}', txt) # U-grave txt = re.sub('Ú|Ú|Ú', r"{U'}", txt) # U-acute txt = re.sub('Û|Û|Û', r'{U^}', txt) # U-circumflex txt = re.sub('Ü|Ü|Ü', r'{U"}', txt) # U-umlaut diff --git a/src/calibre/ebooks/txt/processor.py b/src/calibre/ebooks/txt/processor.py index 45fc22eded..08988eaddc 100644 --- a/src/calibre/ebooks/txt/processor.py +++ b/src/calibre/ebooks/txt/processor.py @@ -234,7 +234,7 @@ def opf_writer(path, opf_name, manifest, spine, mi): def split_utf8(s, n): - """Split UTF-8 s into chunks of maximum length n.""" + '''Split UTF-8 s into chunks of maximum length n.''' if n < 3: raise ValueError(f'Cannot split into chunks of less than {n} < 4 bytes') s = memoryview(s) diff --git a/src/calibre/ebooks/unihandecode/__init__.py b/src/calibre/ebooks/unihandecode/__init__.py index 5bb6156ac5..3abdb631db 100644 --- a/src/calibre/ebooks/unihandecode/__init__.py +++ b/src/calibre/ebooks/unihandecode/__init__.py @@ -1,7 +1,7 @@ __license__ = 'GPL 3' __copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>' __docformat__ = 'restructuredtext en' -__all__ = ["Unihandecoder"] +__all__ = ['Unihandecoder'] ''' Decode unicode text to an ASCII representation of the text. @@ -22,7 +22,7 @@ class Unihandecoder: preferred_encoding = None decoder = None - def __init__(self, lang="zh", encoding='utf-8'): + def __init__(self, lang='zh', encoding='utf-8'): self.preferred_encoding = encoding lang = lang.lower() if lang[:2] == 'ja': diff --git a/src/calibre/ebooks/unihandecode/jadecoder.py b/src/calibre/ebooks/unihandecode/jadecoder.py index cd05c249d8..31fcc11ad5 100644 --- a/src/calibre/ebooks/unihandecode/jadecoder.py +++ b/src/calibre/ebooks/unihandecode/jadecoder.py @@ -70,20 +70,20 @@ class Jadecoder(Unidecoder): # words. Sigh. # https://codeberg.org/miurahr/pykakasi/issues/172 with warnings.catch_warnings(): - warnings.simplefilter("ignore") + warnings.simplefilter('ignore') self.kakasi = kakasi() - self.kakasi.setMode("H","a") # Hiragana to ascii, default: no conversion - self.kakasi.setMode("K","a") # Katakana to ascii, default: no conversion - self.kakasi.setMode("J","a") # Japanese to ascii, default: no conversion - self.kakasi.setMode("r","Hepburn") # default: use Hepburn Roman table - self.kakasi.setMode("s", True) # add space, default: no separator - self.kakasi.setMode("C", True) # capitalize, default: no capitalize + self.kakasi.setMode('H','a') # Hiragana to ascii, default: no conversion + self.kakasi.setMode('K','a') # Katakana to ascii, default: no conversion + self.kakasi.setMode('J','a') # Japanese to ascii, default: no conversion + self.kakasi.setMode('r','Hepburn') # default: use Hepburn Roman table + self.kakasi.setMode('s', True) # add space, default: no separator + self.kakasi.setMode('C', True) # capitalize, default: no capitalize self.conv = self.kakasi.getConverter() def decode(self, text): try: with warnings.catch_warnings(): - warnings.simplefilter("ignore") + warnings.simplefilter('ignore') text = self.conv.do(text) except Exception: pass diff --git a/src/calibre/ebooks/unihandecode/unicodepoints.py b/src/calibre/ebooks/unihandecode/unicodepoints.py index 3573da9614..8f5ee89a17 100644 --- a/src/calibre/ebooks/unihandecode/unicodepoints.py +++ b/src/calibre/ebooks/unihandecode/unicodepoints.py @@ -13,9 +13,9 @@ a single dictionary. CODEPOINTS = { 'x20': [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '', '', '', '', - '-', '-', '-', '-', '--', '--', '||', '_', '\'', '\'', ',', '\'', '"', '"', ',,', '"', + '-', '-', '-', '-', '--', '--', '||', '_', "'", "'", ',', "'", '"', '"', ',,', '"', '+', '++', '*', '*>', '.', '..', '...', '.', '\n', '\n\n', '', '', '', '', '', ' ', - '%0', '%00', '\'', '\'\'', '\'\'\'', '`', '``', '```', '^', '<', '>', '*', '!!', '!?', '-', '_', + '%0', '%00', "'", "''", "'''", '`', '``', '```', '^', '<', '>', '*', '!!', '!?', '-', '_', '-', '^', '***', '--', '/', '-[', ']-', '[?]', '?!', '!?', '7', 'PP', '(]', '[)', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '', '', '', '', '', '', @@ -127,7 +127,7 @@ CODEPOINTS = { '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '', '', '', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', - '[?]', '[?]', '[?]', '[?]', '\'', ',', '[?]', '[?]', '[?]', '[?]', '', '[?]', '[?]', '[?]', '?', '[?]', + '[?]', '[?]', '[?]', '[?]', "'", ',', '[?]', '[?]', '[?]', '[?]', '', '[?]', '[?]', '[?]', '?', '[?]', '[?]', '[?]', '[?]', '[?]', '', '', 'A', ';', 'E', 'E', 'I', '[?]', 'O', '[?]', 'U', 'O', 'I', 'A', 'B', 'G', 'D', 'E', 'Z', 'E', 'Th', 'I', 'K', 'L', 'M', 'N', 'Ks', 'O', 'P', 'R', '[?]', 'S', 'T', 'U', 'Ph', 'Kh', 'Ps', 'O', 'I', 'U', 'a', 'e', 'e', 'i', @@ -176,31 +176,31 @@ CODEPOINTS = { 'x06': [ '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', ',', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', ';', '[?]', '[?]', '[?]', '?', - '[?]', '', 'a', '\'', 'w\'', '', 'y\'', 'a', 'b', '@', 't', 'th', 'j', 'H', 'kh', 'd', + '[?]', '', 'a', "'", "w'", '', "y'", 'a', 'b', '@', 't', 'th', 'j', 'H', 'kh', 'd', 'dh', 'r', 'z', 's', 'sh', 'S', 'D', 'T', 'Z', '`', 'G', '[?]', '[?]', '[?]', '[?]', '[?]', '', 'f', 'q', 'k', 'l', 'm', 'n', 'h', 'w', '~', 'y', 'an', 'un', 'in', 'a', 'u', - 'i', 'W', '', '', '\'', '\'', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', + 'i', 'W', '', '', "'", "'", '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '%', '.', ',', '*', '[?]', '[?]', - '', '\'', '\'', '\'', '', '\'', '\'w', '\'u', '\'y', 'tt', 'tth', 'b', 't', 'T', 'p', 'th', - 'bh', '\'h', 'H', 'ny', 'dy', 'H', 'ch', 'cch', 'dd', 'D', 'D', 'Dt', 'dh', 'ddh', 'd', 'D', + '', "'", "'", "'", '', "'", "'w", "'u", "'y", 'tt', 'tth', 'b', 't', 'T', 'p', 'th', + 'bh', "'h", 'H', 'ny', 'dy', 'H', 'ch', 'cch', 'dd', 'D', 'D', 'Dt', 'dh', 'ddh', 'd', 'D', 'D', 'rr', 'R', 'R', 'R', 'R', 'R', 'R', 'j', 'R', 'S', 'S', 'S', 'S', 'S', 'T', 'GH', 'F', 'F', 'F', 'v', 'f', 'ph', 'Q', 'Q', 'kh', 'k', 'K', 'K', 'ng', 'K', 'g', 'G', 'N', 'G', 'G', 'G', 'L', 'L', 'L', 'L', 'N', 'N', 'N', 'N', 'N', 'h', 'Ch', 'hy', 'h', 'H', '@', 'W', 'oe', 'oe', 'u', 'yu', 'yu', 'W', 'v', 'y', 'Y', 'Y', 'W', - '', '', 'y', 'y\'', '.', 'ae', '', '', '', '', '', '', '', '@', '#', '', + '', '', 'y', "y'", '.', 'ae', '', '', '', '', '', '', '', '@', '#', '', '', '', '', '', '', '', '', '', '', '^', '', '', '', '', '[?]', '[?]', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'Sh', 'D', 'Gh', '&', '+m', ], 'x07': [ '//', '/', ',', '!', '!', '-', ',', ',', ';', '?', '~', '\\{', '\\}', '*', '[?]', '', - '\'', '', 'b', 'g', 'g', 'd', 'd', 'h', 'w', 'z', 'H', 't', 't', 'y', 'yh', 'k', + "'", '', 'b', 'g', 'g', 'd', 'd', 'h', 'w', 'z', 'H', 't', 't', 'y', 'yh', 'k', 'l', 'm', 'n', 's', 's', '`', 'p', 'p', 'S', 'q', 'r', 'sh', 't', '[?]', '[?]', '[?]', 'a', 'a', 'a', 'A', 'A', 'A', 'e', 'e', 'e', 'E', 'i', 'i', 'u', 'u', 'u', 'o', - '', '`', '\'', '', '', 'X', 'Q', '@', '@', '|', '+', '[?]', '[?]', '[?]', '[?]', '[?]', + '', '`', "'", '', '', 'X', 'Q', '@', '@', '|', '+', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', - 'h', 'sh', 'n', 'r', 'b', 'L', 'k', '\'', 'v', 'm', 'f', 'dh', 'th', 'l', 'g', 'ny', + 'h', 'sh', 'n', 'r', 'b', 'L', 'k', "'", 'v', 'm', 'f', 'dh', 'th', 'l', 'g', 'ny', 's', 'd', 'z', 't', 'y', 'p', 'j', 'ch', 'tt', 'hh', 'kh', 'th', 'z', 'sh', 's', 'd', 't', 'z', '`', 'gh', 'q', 'w', 'a', 'aa', 'i', 'ee', 'u', 'oo', 'e', 'ey', 'o', 'oa', '', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', @@ -302,17 +302,17 @@ CODEPOINTS = { 'x04': [ 'Ie', 'Io', 'Dj', 'Gj', 'Ie', 'Dz', 'I', 'Yi', 'J', 'Lj', 'Nj', 'Tsh', 'Kj', 'I', 'U', 'Dzh', 'A', 'B', 'V', 'G', 'D', 'Ie', 'Zh', 'Z', 'I', 'I', 'K', 'L', 'M', 'N', 'O', 'P', - 'R', 'S', 'T', 'U', 'F', 'Kh', 'Ts', 'Ch', 'Sh', 'Shch', '', 'Y', '\'', 'E', 'Iu', 'Ia', + 'R', 'S', 'T', 'U', 'F', 'Kh', 'Ts', 'Ch', 'Sh', 'Shch', '', 'Y', "'", 'E', 'Iu', 'Ia', 'a', 'b', 'v', 'gh', 'd', 'ie', 'zh', 'z', 'i', 'i', 'k', 'l', 'm', 'n', 'o', 'p', - 'r', 's', 't', 'u', 'f', 'kh', 'ts', 'ch', 'sh', 'shch', '', 'y', '\'', 'e', 'iu', 'ia', + 'r', 's', 't', 'u', 'f', 'kh', 'ts', 'ch', 'sh', 'shch', '', 'y', "'", 'e', 'iu', 'ia', 'ie', 'io', 'dj', 'gj', 'ie', 'dz', 'i', 'yi', 'j', 'lj', 'nj', 'tsh', 'kj', 'i', 'u', 'dzh', 'O', 'o', 'E', 'e', 'Ie', 'ie', 'E', 'e', 'Ie', 'ie', 'O', 'o', 'Io', 'io', 'Ks', 'ks', 'Ps', 'ps', 'F', 'f', 'Y', 'y', 'Y', 'y', 'u', 'u', 'O', 'o', 'O', 'o', 'Ot', 'ot', - 'Q', 'q', '*1000*', '', '', '', '', '[?]', '*100.000*', '*1.000.000*', '[?]', '[?]', '"', '"', 'R\'', 'r\'', - 'G\'', 'g\'', 'G\'', 'g\'', 'G\'', 'g\'', 'Zh\'', 'zh\'', 'Z\'', 'z\'', 'K\'', 'k\'', 'K\'', 'k\'', 'K\'', 'k\'', - 'K\'', 'k\'', 'N\'', 'n\'', 'Ng', 'ng', 'P\'', 'p\'', 'Kh', 'kh', 'S\'', 's\'', 'T\'', 't\'', 'U', 'u', - 'U\'', 'u\'', 'Kh\'', 'kh\'', 'Tts', 'tts', 'Ch\'', 'ch\'', 'Ch\'', 'ch\'', 'H', 'h', 'Ch', 'ch', 'Ch\'', 'ch\'', - '`', 'Zh', 'zh', 'K\'', 'k\'', '[?]', '[?]', 'N\'', 'n\'', '[?]', '[?]', 'Ch', 'ch', '[?]', '[?]', '[?]', + 'Q', 'q', '*1000*', '', '', '', '', '[?]', '*100.000*', '*1.000.000*', '[?]', '[?]', '"', '"', "R'", "r'", + "G'", "g'", "G'", "g'", "G'", "g'", "Zh'", "zh'", "Z'", "z'", "K'", "k'", "K'", "k'", "K'", "k'", + "K'", "k'", "N'", "n'", 'Ng', 'ng', "P'", "p'", 'Kh', 'kh', "S'", "s'", "T'", "t'", 'U', 'u', + "U'", "u'", "Kh'", "kh'", 'Tts', 'tts', "Ch'", "ch'", "Ch'", "ch'", 'H', 'h', 'Ch', 'ch', "Ch'", "ch'", + '`', 'Zh', 'zh', "K'", "k'", '[?]', '[?]', "N'", "n'", '[?]', '[?]', 'Ch', 'ch', '[?]', '[?]', '[?]', 'a', 'a', 'A', 'a', 'Ae', 'ae', 'Ie', 'ie', '@', '@', '@', '@', 'Zh', 'zh', 'Z', 'z', 'Dz', 'dz', 'I', 'i', 'I', 'i', 'O', 'o', 'O', 'o', 'O', 'o', 'E', 'e', 'U', 'u', 'U', 'u', 'U', 'u', 'Ch', 'ch', '[?]', '[?]', 'Y', 'y', '[?]', '[?]', '[?]', '[?]', '[?]', @@ -465,7 +465,7 @@ CODEPOINTS = { 'k', 'kh', 'g', 'gh', 'ng', 'c', 'ch', 'j', 'jh', 'ny', 'nny', 'tt', 'tth', 'dd', 'ddh', 'nn', 'tt', 'th', 'd', 'dh', 'n', 'p', 'ph', 'b', 'bh', 'm', 'y', 'r', 'l', 'w', 's', 'h', 'll', 'a', '[?]', 'i', 'ii', 'u', 'uu', 'e', '[?]', 'o', 'au', '[?]', 'aa', 'i', 'ii', 'u', - 'uu', 'e', 'ai', '[?]', '[?]', '[?]', 'N', '\'', ':', '', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', + 'uu', 'e', 'ai', '[?]', '[?]', '[?]', 'N', "'", ':', '', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' / ', ' // ', 'n*', 'r*', 'l*', 'e*', 'sh', 'ss', 'R', 'RR', 'L', 'LL', 'R', 'RR', 'L', 'LL', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', @@ -473,10 +473,10 @@ CODEPOINTS = { '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', 'A', 'B', 'G', 'D', 'E', 'V', 'Z', 'T`', 'I', 'K', 'L', 'M', 'N', 'O', 'P', 'Zh', - 'R', 'S', 'T', 'U', 'P`', 'K`', 'G\'', 'Q', 'Sh', 'Ch`', 'C`', 'Z\'', 'C', 'Ch', 'X', 'J', + 'R', 'S', 'T', 'U', 'P`', 'K`', "G'", 'Q', 'Sh', 'Ch`', 'C`', "Z'", 'C', 'Ch', 'X', 'J', 'H', 'E', 'Y', 'W', 'Xh', 'OE', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', 'a', 'b', 'g', 'd', 'e', 'v', 'z', 't`', 'i', 'k', 'l', 'm', 'n', 'o', 'p', 'zh', - 'r', 's', 't', 'u', 'p`', 'k`', 'g\'', 'q', 'sh', 'ch`', 'c`', 'z\'', 'c', 'ch', 'x', 'j', + 'r', 's', 't', 'u', 'p`', 'k`', "g'", 'q', 'sh', 'ch`', 'c`', "z'", 'c', 'ch', 'x', 'j', 'h', 'e', 'y', 'w', 'xh', 'oe', 'f', '[?]', '[?]', '[?]', '[?]', ' // ', '[?]', '[?]', '[?]', ], 'x1f': [ @@ -491,11 +491,11 @@ CODEPOINTS = { 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', - 'a', 'a', 'a', 'a', 'a', '[?]', 'a', 'a', 'A', 'A', 'A', 'A', 'A', '\'', 'i', '\'', - '~', '"~', 'e', 'e', 'e', '[?]', 'e', 'e', 'E', 'E', 'E', 'E', 'E', '\'`', '\'\'', '\'~', - 'i', 'i', 'i', 'i', '[?]', '[?]', 'i', 'i', 'I', 'I', 'I', 'I', '[?]', '`\'', '`\'', '`~', + 'a', 'a', 'a', 'a', 'a', '[?]', 'a', 'a', 'A', 'A', 'A', 'A', 'A', "'", 'i', "'", + '~', '"~', 'e', 'e', 'e', '[?]', 'e', 'e', 'E', 'E', 'E', 'E', 'E', "'`", "''", "'~", + 'i', 'i', 'i', 'i', '[?]', '[?]', 'i', 'i', 'I', 'I', 'I', 'I', '[?]', "`'", "`'", '`~', 'u', 'u', 'u', 'u', 'R', 'R', 'u', 'u', 'U', 'U', 'U', 'U', 'R', '"`', '"\'', '`', - '[?]', '[?]', 'o', 'o', 'o', '[?]', 'o', 'o', 'O', 'O', 'O', 'O', 'O', '\'', '`', + '[?]', '[?]', 'o', 'o', 'o', '[?]', 'o', 'o', 'O', 'O', 'O', 'O', 'O', "'", '`', ], 'x31': [ '[?]', '[?]', '[?]', '[?]', '[?]', 'B', 'P', 'M', 'F', 'D', 'T', 'N', 'L', 'G', 'K', 'H', @@ -526,7 +526,7 @@ CODEPOINTS = { 'r', 'ch', 'zh', 'i', 'k', 'r', 'f', 'zh', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', 'H', 'X', 'W', 'M', ' 3 ', ' 333 ', 'a', 'i', 'k', 'ng', 'c', 'tt', 'tth', 'dd', 'nn', 't', 'd', 'p', 'ph', 'ss', 'zh', 'z', 'a', 't', 'zh', 'gh', 'ng', 'c', 'jh', 'tta', 'ddh', - 't', 'dh', 'ss', 'cy', 'zh', 'z', 'u', 'y', 'bh', '\'', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', + 't', 'dh', 'ss', 'cy', 'zh', 'z', 'u', 'y', 'bh', "'", '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', @@ -574,7 +574,7 @@ CODEPOINTS = { 'D', 'd', 'E', 'e', 'E', 'e', 'E', 'e', 'E', 'e', 'E', 'e', 'G', 'g', 'G', 'g', 'G', 'g', 'G', 'g', 'H', 'h', 'H', 'h', 'I', 'i', 'I', 'i', 'I', 'i', 'I', 'i', 'I', 'i', 'IJ', '', 'J', 'j', 'K', 'k', 'k', 'L', 'l', 'L', 'l', 'L', 'l', 'L', - 'l', 'L', 'l', 'N', 'n', 'N', 'n', 'N', 'n', '\'n', 'ng', 'NG', 'O', 'o', 'O', 'o', + 'l', 'L', 'l', 'N', 'n', 'N', 'n', 'N', 'n', "'n", 'ng', 'NG', 'O', 'o', 'O', 'o', 'O', 'o', 'OE', 'oe', 'R', 'r', 'R', 'r', 'R', 'r', 'S', 's', 'S', 's', 'S', 's', 'S', 's', 'T', 't', 'T', 't', 'T', 't', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'W', 'w', 'Y', 'y', 'Y', 'Z', 'z', 'Z', 'z', 'Z', 'z', 's', @@ -681,7 +681,7 @@ CODEPOINTS = { '[?]', 'k', 'kh', 'kh', 'kh', 'kh', 'kh', 'ng', 'cch', 'ch', 'ch', 'ch', 'ch', 'y', 'd', 't', 'th', 'th', 'th', 'n', 'd', 't', 'th', 'th', 'th', 'n', 'b', 'p', 'ph', 'f', 'ph', 'f', 'ph', 'm', 'y', 'r', 'R', 'l', 'L', 'w', 's', 's', 's', 'h', 'l', '`', 'h', '~', - 'a', 'a', 'aa', 'am', 'i', 'ii', 'ue', 'uue', 'u', 'uu', '\'', '[?]', '[?]', '[?]', '[?]', 'Bh.', + 'a', 'a', 'aa', 'am', 'i', 'ii', 'ue', 'uue', 'u', 'uu', "'", '[?]', '[?]', '[?]', '[?]', 'Bh.', 'e', 'ae', 'o', 'ai', 'ai', 'ao', '+', '', '', '', '', '', '', 'M', '', ' * ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' // ', ' /// ', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', @@ -796,7 +796,7 @@ CODEPOINTS = { 'ta', 'tu', 'ti', 'taa', 'tee', 'te', 'to', 'twa', 'ca', 'cu', 'ci', 'caa', 'cee', 'ce', 'co', 'cwa', 'xa', 'xu', 'xi', 'xaa', 'xee', 'xe', 'xo', '[?]', 'xwa', '[?]', 'xwi', 'xwaa', 'xwee', 'xwe', '[?]', '[?]', 'na', 'nu', 'ni', 'naa', 'nee', 'ne', 'no', 'nwa', 'nya', 'nyu', 'nyi', 'nyaa', 'nyee', 'nye', 'nyo', 'nywa', - '\'a', '\'u', '[?]', '\'aa', '\'ee', '\'e', '\'o', '\'wa', 'ka', 'ku', 'ki', 'kaa', 'kee', 'ke', 'ko', '[?]', + "'a", "'u", '[?]', "'aa", "'ee", "'e", "'o", "'wa", 'ka', 'ku', 'ki', 'kaa', 'kee', 'ke', 'ko', '[?]', 'kwa', '[?]', 'kwi', 'kwaa', 'kwee', 'kwe', '[?]', '[?]', 'kxa', 'kxu', 'kxi', 'kxaa', 'kxee', 'kxe', 'kxo', '[?]', 'kxwa', '[?]', 'kxwi', 'kxwaa', 'kxwee', 'kxwe', '[?]', '[?]', 'wa', 'wu', 'wi', 'waa', 'wee', 'we', 'wo', '[?]', '`a', '`u', '`i', '`aa', '`ee', '`e', '`o', '[?]', 'za', 'zu', 'zi', 'zaa', 'zee', 'ze', 'zo', 'zwa', @@ -840,7 +840,7 @@ CODEPOINTS = { 'ggwe', 'ggweg', 'ggwegg', 'ggwegs', 'ggwen', 'ggwenj', 'ggwenh', 'ggwed', 'ggwel', 'ggwelg', 'ggwelm', 'ggwelb', 'ggwels', 'ggwelt', 'ggwelp', 'ggwelh', ], 'x28': [ - ' ', 'a', '1', 'b', '\'', 'k', '2', 'l', '@', 'c', 'i', 'f', '/', 'm', 's', 'p', + ' ', 'a', '1', 'b', "'", 'k', '2', 'l', '@', 'c', 'i', 'f', '/', 'm', 's', 'p', '"', 'e', '3', 'h', '9', 'o', '6', 'r', '^', 'd', 'j', 'g', '>', 'n', 't', 'q', ',', '*', '5', '<', '-', 'u', '8', 'v', '.', '%', '[', '\\$', '+', 'x', '!', '&', ';', ':', '4', '\\', '0', 'z', '7', '(', '_', '?', 'w', ']', '#', 'y', ')', '=', @@ -895,7 +895,7 @@ CODEPOINTS = { ], 'x14': [ '[?]', 'e', 'aai', 'i', 'ii', 'o', 'oo', 'oo', 'ee', 'i', 'a', 'aa', 'we', 'we', 'wi', 'wi', - 'wii', 'wii', 'wo', 'wo', 'woo', 'woo', 'woo', 'wa', 'wa', 'waa', 'waa', 'waa', 'ai', 'w', '\'', 't', + 'wii', 'wii', 'wo', 'wo', 'woo', 'woo', 'woo', 'wa', 'wa', 'waa', 'waa', 'waa', 'ai', 'w', "'", 't', 'k', 'sh', 's', 'n', 'w', 'n', '[?]', 'w', 'c', '?', 'l', 'en', 'in', 'on', 'an', 'pe', 'paai', 'pi', 'pii', 'po', 'poo', 'poo', 'hee', 'hi', 'pa', 'paa', 'pwe', 'pwe', 'pwi', 'pwi', 'pwii', 'pwii', 'pwo', 'pwo', 'pwoo', 'pwoo', 'pwa', 'pwa', 'pwaa', 'pwaa', 'pwaa', 'p', 'p', 'h', 'te', 'taai', 'ti', 'tii', @@ -914,7 +914,7 @@ CODEPOINTS = { 'x00': [ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', - ' ', '!', '"', '#', '\\$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', + ' ', '!', '"', '#', '\\$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ']', '\\', ']', '^', '_', @@ -923,7 +923,7 @@ CODEPOINTS = { '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ' ', '!', 'C/', 'PS', '\\$?', 'Y=', '|', 'SS', '"', '(c)', 'a', '<<', '!', '', '(r)', '-', - 'deg', '+-', '2', '3', '\'', 'u', 'P', '*', ',', '1', 'o', '>>', '1/4', '1/2', '3/4', '?', + 'deg', '+-', '2', '3', "'", 'u', 'P', '*', ',', '1', 'o', '>>', '1/4', '1/2', '3/4', '?', 'A', 'A', 'A', 'A', 'A', 'A', 'AE', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', 'O', 'U', 'U', 'U', 'U', 'U', 'Th', 'ss', 'a', 'a', 'a', 'a', 'a', 'a', 'ae', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', @@ -971,17 +971,17 @@ CODEPOINTS = { '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', 'A', 'B', 'G', 'D', 'E', 'Z', 'E', 'E', 'T`', 'Zh', 'I', 'L', 'Kh', 'Ts', 'K', 'H', 'Dz', 'Gh', 'Ch', 'M', 'Y', 'N', 'Sh', 'O', 'Ch`', 'P', 'J', 'Rh', 'S', 'V', 'T', - 'R', 'Ts`', 'W', 'P`', 'K`', 'O', 'F', '[?]', '[?]', '<', '\'', '/', '!', ',', '?', '.', + 'R', 'Ts`', 'W', 'P`', 'K`', 'O', 'F', '[?]', '[?]', '<', "'", '/', '!', ',', '?', '.', '[?]', 'a', 'b', 'g', 'd', 'e', 'z', 'e', 'e', 't`', 'zh', 'i', 'l', 'kh', 'ts', 'k', 'h', 'dz', 'gh', 'ch', 'm', 'y', 'n', 'sh', 'o', 'ch`', 'p', 'j', 'rh', 's', 'v', 't', 'r', 'ts`', 'w', 'p`', 'k`', 'o', 'f', 'ew', '[?]', '.', '-', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '[?]', '', '', '', '', '', '', '', '', '', '', '', '', '', - '@', 'e', 'a', 'o', 'i', 'e', 'e', 'a', 'a', 'o', '[?]', 'u', '\'', '', '', '', + '@', 'e', 'a', 'o', 'i', 'e', 'e', 'a', 'a', 'o', '[?]', 'u', "'", '', '', '', '', '', '', ':', '', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '', 'b', 'g', 'd', 'h', 'v', 'z', 'kh', 't', 'y', 'k', 'k', 'l', 'm', 'm', 'n', 'n', 's', '`', 'p', 'p', 'ts', 'ts', 'q', 'r', 'sh', 't', '[?]', '[?]', '[?]', '[?]', '[?]', - 'V', 'oy', 'i', '\'', '"', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', + 'V', 'oy', 'i', "'", '"', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', ], 'xce': [ 'cwik', 'cwit', 'cwip', 'cwih', 'cyu', 'cyug', 'cyugg', 'cyugs', 'cyun', 'cyunj', 'cyunh', 'cyud', 'cyul', 'cyulg', 'cyulm', 'cyulb', @@ -1121,9 +1121,9 @@ CODEPOINTS = { 'R', 'R', 's', 'S', 'j', 'S', 'S', 't', 't', 'U', 'U', 'v', '^', 'W', 'Y', 'Y', 'z', 'z', 'Z', 'Z', '?', '?', '?', 'C', '@', 'B', 'E', 'G', 'H', 'j', 'k', 'L', 'q', '?', '?', 'dz', 'dZ', 'dz', 'ts', 'tS', 'tC', 'fN', 'ls', 'lz', 'WW', ']]', '[?]', '[?]', - 'k', 'h', 'j', 'r', 'r', 'r', 'r', 'w', 'y', '\'', '"', '`', '\'', '`', '`', '\'', - '?', '?', '<', '>', '^', 'V', '^', 'V', '\'', '-', '/', '\\', ',', '_', '\\', '/', - ':', '.', '`', '\'', '^', 'V', '+', '-', 'V', '.', '@', ',', '~', '"', 'R', 'X', + 'k', 'h', 'j', 'r', 'r', 'r', 'r', 'w', 'y', "'", '"', '`', "'", '`', '`', "'", + '?', '?', '<', '>', '^', 'V', '^', 'V', "'", '-', '/', '\\', ',', '_', '\\', '/', + ':', '.', '`', "'", '^', 'V', '+', '-', 'V', '.', '@', ',', '~', '"', 'R', 'X', 'G', 'l', 's', 'x', '?', '', '', '', '', '', '', '', 'V', '=', '"', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', ], @@ -1159,7 +1159,7 @@ CODEPOINTS = { 'h', 'l', 'q', 'a', 'aa', 'i', 'ii', 'u', 'uk', 'uu', 'uuv', 'ry', 'ryy', 'ly', 'lyy', 'e', 'ai', 'oo', 'oo', 'au', 'a', 'aa', 'aa', 'i', 'ii', 'y', 'yy', 'u', 'uu', 'ua', 'oe', 'ya', 'ie', 'e', 'ae', 'ai', 'oo', 'au', 'M', 'H', 'a`', '', '', '', 'r', '', '!', '', - '', '', '', '', '.', ' // ', ':', '+', '++', ' * ', ' /// ', 'KR', '\'', '[?]', '[?]', '[?]', + '', '', '', '', '.', ' // ', ':', '+', '++', ' * ', ' /// ', 'KR', "'", '[?]', '[?]', '[?]', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', ], @@ -1167,7 +1167,7 @@ CODEPOINTS = { '[?]', 'N', 'N', 'H', '[?]', 'a', 'aa', 'i', 'ii', 'u', 'uu', 'R', 'L', '[?]', '[?]', 'e', 'ai', '[?]', '[?]', 'o', 'au', 'k', 'kh', 'g', 'gh', 'ng', 'c', 'ch', 'j', 'jh', 'ny', 'tt', 'tth', 'dd', 'ddh', 'nn', 't', 'th', 'd', 'dh', 'n', '[?]', 'p', 'ph', 'b', 'bh', 'm', 'y', - 'r', '[?]', 'l', 'll', '[?]', '', 'sh', 'ss', 's', 'h', '[?]', '[?]', '\'', '\'', 'aa', 'i', + 'r', '[?]', 'l', 'll', '[?]', '', 'sh', 'ss', 's', 'h', '[?]', '[?]', "'", "'", 'aa', 'i', 'ii', 'u', 'uu', 'R', '[?]', '[?]', '[?]', 'e', 'ai', '[?]', '[?]', 'o', 'au', '', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '+', '+', '[?]', '[?]', '[?]', '[?]', 'rr', 'rh', '[?]', 'yy', 'RR', 'LL', '[?]', '[?]', '[?]', '[?]', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', @@ -1188,12 +1188,12 @@ CODEPOINTS = { '6.5', '7.5', '8.5', '-.5', '+', '*', '^', '_', '', '~', '[?]', ']', '[[', ']]', '', '', 'k', 'kh', 'g', 'gh', 'ng', 'c', 'ch', 'j', '[?]', 'ny', 'tt', 'tth', 'dd', 'ddh', 'nn', 't', 'th', 'd', 'dh', 'n', 'p', 'ph', 'b', 'bh', 'm', 'ts', 'tsh', 'dz', 'dzh', 'w', 'zh', 'z', - '\'', 'y', 'r', 'l', 'sh', 'ssh', 's', 'h', 'a', 'kss', 'r', '[?]', '[?]', '[?]', '[?]', '[?]', + "'", 'y', 'r', 'l', 'sh', 'ssh', 's', 'h', 'a', 'kss', 'r', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', 'aa', 'i', 'ii', 'u', 'uu', 'R', 'RR', 'L', 'LL', 'e', 'ee', 'o', 'oo', 'M', 'H', 'i', 'ii', '', '', '', '', '', '', '', '', '', '', '[?]', '[?]', '[?]', '[?]', 'k', 'kh', 'g', 'gh', 'ng', 'c', 'ch', 'j', '[?]', 'ny', 'tt', 'tth', 'dd', 'ddh', 'nn', 't', 'th', 'd', 'dh', 'n', 'p', 'ph', 'b', 'bh', 'm', 'ts', 'tsh', 'dz', 'dzh', 'w', 'zh', 'z', - '\'', 'y', 'r', 'l', 'sh', 'ss', 's', 'h', 'a', 'kss', 'w', 'y', 'r', '[?]', 'X', ' :X: ', + "'", 'y', 'r', 'l', 'sh', 'ss', 's', 'h', 'a', 'kss', 'w', 'y', 'r', '[?]', 'X', ' :X: ', ' /O/ ', ' /o/ ', ' \\o\\ ', ' (O) ', '', '', '', '', '', '', '', '', '', '[?]', '[?]', '', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', @@ -1254,7 +1254,7 @@ CODEPOINTS = { 'maels', 'maelt', 'maelp', 'maelh', 'maem', 'maeb', 'maebs', 'maes', 'maess', 'maeng', 'maej', 'maec', 'maek', 'maet', 'maep', 'maeh', ], 'xff': [ - '[?]', '!', '"', '#', '\\$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', + '[?]', '!', '"', '#', '\\$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', @@ -1293,19 +1293,19 @@ CODEPOINTS = { '[?]', 'N', 'N', 'H', '[?]', 'a', 'aa', 'i', 'ii', 'u', 'uu', 'R', 'L', 'eN', 'e', 'e', 'ai', 'oN', 'o', 'o', 'au', 'k', 'kh', 'g', 'gh', 'ng', 'c', 'ch', 'j', 'jh', 'ny', 'tt', 'tth', 'dd', 'ddh', 'nn', 't', 'th', 'd', 'dh', 'n', 'nnn', 'p', 'ph', 'b', 'bh', 'm', 'y', - 'r', 'rr', 'l', 'l', 'lll', 'v', 'sh', 'ss', 's', 'h', '[?]', '[?]', '\'', '\'', 'aa', 'i', + 'r', 'rr', 'l', 'l', 'lll', 'v', 'sh', 'ss', 's', 'h', '[?]', '[?]', "'", "'", 'aa', 'i', 'ii', 'u', 'uu', 'R', 'RR', 'eN', 'e', 'e', 'ai', 'oN', 'o', 'o', 'au', '', '[?]', '[?]', - 'AUM', '\'', '\'', '`', '\'', '[?]', '[?]', '[?]', 'q', 'khh', 'ghh', 'z', 'dddh', 'rh', 'f', 'yy', + 'AUM', "'", "'", '`', "'", '[?]', '[?]', '[?]', 'q', 'khh', 'ghh', 'z', 'dddh', 'rh', 'f', 'yy', 'RR', 'LL', 'L', 'LL', ' / ', ' // ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', 'N', 'N', 'H', '[?]', 'a', 'aa', 'i', 'ii', 'u', 'uu', 'R', 'RR', '[?]', '[?]', 'e', 'ai', '[?]', '[?]', 'o', 'au', 'k', 'kh', 'g', 'gh', 'ng', 'c', 'ch', 'j', 'jh', 'ny', 'tt', 'tth', 'dd', 'ddh', 'nn', 't', 'th', 'd', 'dh', 'n', '[?]', 'p', 'ph', 'b', 'bh', 'm', 'y', - 'r', '[?]', 'l', '[?]', '[?]', '[?]', 'sh', 'ss', 's', 'h', '[?]', '[?]', '\'', '[?]', 'aa', 'i', + 'r', '[?]', 'l', '[?]', '[?]', '[?]', 'sh', 'ss', 's', 'h', '[?]', '[?]', "'", '[?]', 'aa', 'i', 'ii', 'u', 'uu', 'R', 'RR', '[?]', '[?]', 'e', 'ai', '[?]', '[?]', 'o', 'au', '', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '+', '[?]', '[?]', '[?]', '[?]', 'rr', 'rh', '[?]', 'yy', 'RR', 'LL', 'L', 'LL', '[?]', '[?]', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'r\'', 'r`', 'Rs', 'Rs', '1/', '2/', '3/', '4/', ' 1 - 1/', '/16', '', '[?]', '[?]', '[?]', '[?]', + "r'", 'r`', 'Rs', 'Rs', '1/', '2/', '3/', '4/', ' 1 - 1/', '/16', '', '[?]', '[?]', '[?]', '[?]', ], 'xb2': [ 'nyok', 'nyot', 'nyop', 'nyoh', 'nu', 'nug', 'nugg', 'nugs', 'nun', 'nunj', 'nunh', 'nud', 'nul', 'nulg', 'nulm', 'nulb', @@ -1329,7 +1329,7 @@ CODEPOINTS = { '[?]', '[?]', 'N', '[?]', '[?]', 'a', 'aa', 'i', 'ii', 'u', 'uu', '[?]', '[?]', '[?]', '[?]', 'ee', 'ai', '[?]', '[?]', 'oo', 'au', 'k', 'kh', 'g', 'gh', 'ng', 'c', 'ch', 'j', 'jh', 'ny', 'tt', 'tth', 'dd', 'ddh', 'nn', 't', 'th', 'd', 'dh', 'n', '[?]', 'p', 'ph', 'b', 'bb', 'm', 'y', - 'r', '[?]', 'l', 'll', '[?]', 'v', 'sh', '[?]', 's', 'h', '[?]', '[?]', '\'', '[?]', 'aa', 'i', + 'r', '[?]', 'l', 'll', '[?]', 'v', 'sh', '[?]', 's', 'h', '[?]', '[?]', "'", '[?]', 'aa', 'i', 'ii', 'u', 'uu', '[?]', '[?]', '[?]', '[?]', 'ee', 'ai', '[?]', '[?]', 'oo', 'au', '', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', 'khh', 'ghh', 'z', 'rr', '[?]', 'f', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', @@ -1337,7 +1337,7 @@ CODEPOINTS = { '[?]', 'N', 'N', 'H', '[?]', 'a', 'aa', 'i', 'ii', 'u', 'uu', 'R', '[?]', 'eN', '[?]', 'e', 'ai', 'oN', '[?]', 'o', 'au', 'k', 'kh', 'g', 'gh', 'ng', 'c', 'ch', 'j', 'jh', 'ny', 'tt', 'tth', 'dd', 'ddh', 'nn', 't', 'th', 'd', 'dh', 'n', '[?]', 'p', 'ph', 'b', 'bh', 'm', 'ya', - 'r', '[?]', 'l', 'll', '[?]', 'v', 'sh', 'ss', 's', 'h', '[?]', '[?]', '\'', '\'', 'aa', 'i', + 'r', '[?]', 'l', 'll', '[?]', 'v', 'sh', 'ss', 's', 'h', '[?]', '[?]', "'", "'", 'aa', 'i', 'ii', 'u', 'uu', 'R', 'RR', 'eN', '[?]', 'e', 'ai', 'oN', '[?]', 'o', 'au', '', '[?]', '[?]', 'AUM', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', 'RR', '[?]', '[?]', '[?]', '[?]', '[?]', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', @@ -1651,7 +1651,7 @@ CODEPOINTS = { ], 'x30': [ ' ', ', ', '. ', '"', '[JIS]', '"', '/', '0', '<', '> ', '<<', '>> ', '[', '] ', '\\{', '\\} ', - '[(', ')] ', '@', 'X ', '[', '] ', '[[', ']] ', '((', ')) ', '[[', ']] ', '~ ', '``', '\'\'', ',,', + '[(', ')] ', '@', 'X ', '[', '] ', '[[', ']] ', '((', ')) ', '[[', ']] ', '~ ', '``', "''", ',,', '@', '1', '2', '3', '4', '5', '6', '7', '8', '9', '', '', '', '', '', '', '~', '+', '+', '+', '+', '', '@', ' // ', '+10+', '+20+', '+30+', '[?]', '[?]', '[?]', '', '', '[?]', 'a', 'a', 'i', 'i', 'u', 'u', 'e', 'e', 'o', 'o', 'ka', 'ga', 'ki', 'gi', 'ku', diff --git a/src/calibre/ebooks/unihandecode/unidecoder.py b/src/calibre/ebooks/unihandecode/unidecoder.py index a4a3ba9a5a..6d7c9f16f7 100644 --- a/src/calibre/ebooks/unihandecode/unidecoder.py +++ b/src/calibre/ebooks/unihandecode/unidecoder.py @@ -94,7 +94,7 @@ class Unidecoder: ''' # Code groups within CODEPOINTS take the form 'xAB' if not isinstance(character, str): - character = str(character, "utf-8") + character = str(character, 'utf-8') return 'x%02x' % (ord(character) >> 8) def grouped_point(self, character): @@ -103,5 +103,5 @@ class Unidecoder: the group character is a part of. ''' if not isinstance(character, str): - character = str(character, "utf-8") + character = str(character, 'utf-8') return ord(character) & 255 diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 9371ba8be4..f43818b891 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -1,7 +1,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' -""" The GUI """ +''' The GUI ''' import glob import os @@ -469,7 +469,7 @@ def create_defs(): defs['books_autoscroll_time'] = 2.0 defs['edit_metadata_single_use_2_cols_for_custom_fields'] = True defs['edit_metadata_elide_labels'] = True - defs['edit_metadata_elision_point'] = "right" + defs['edit_metadata_elision_point'] = 'right' defs['edit_metadata_bulk_cc_label_length'] = 25 defs['edit_metadata_single_cc_label_length'] = 12 defs['edit_metadata_templates_only_F2_on_booklist'] = False @@ -1030,7 +1030,7 @@ def pixmap_to_data(pixmap, format='JPEG', quality=None): Return the QPixmap pixmap as a string saved in the specified format. ''' if quality is None: - if format.upper() == "PNG": + if format.upper() == 'PNG': # For some reason on windows with Qt 5.6 using a quality of 90 # generates invalid PNG data. Many other quality values work # but we use -1 for the default quality which is most likely to @@ -1551,7 +1551,7 @@ def open_url(qurl): # QDesktopServices::openUrl() ensure_app() cmd = ['xdg-open', qurl.toLocalFile() if qurl.isLocalFile() else qurl.toString(QUrl.ComponentFormattingOption.FullyEncoded)] - if isfrozen and QApplication.instance().platformName() == "wayland": + if isfrozen and QApplication.instance().platformName() == 'wayland': # See https://bugreports.qt.io/browse/QTBUG-119438 run_cmd(cmd) ok = True diff --git a/src/calibre/gui2/actions/all_actions.py b/src/calibre/gui2/actions/all_actions.py index 4e2b6ae31c..2c91f85e7c 100644 --- a/src/calibre/gui2/actions/all_actions.py +++ b/src/calibre/gui2/actions/all_actions.py @@ -15,8 +15,8 @@ class AllGUIActions(InterfaceAction): name = 'All GUI actions' action_spec = (_('All actions'), 'wizard.png', - _("Show a menu of all available actions, including from third party plugins.\nThis menu " - "is not available when looking at books on a device"), None) + _('Show a menu of all available actions, including from third party plugins.\nThis menu ' + 'is not available when looking at books on a device'), None) action_type = 'current' popup_type = QToolButton.ToolButtonPopupMode.InstantPopup @@ -34,7 +34,7 @@ class AllGUIActions(InterfaceAction): menu=self.hidden_menu, unique_name='Main window layout', shortcut='Ctrl+F1', - text=_("Show a menu of all available actions."), + text=_('Show a menu of all available actions.'), icon='wizard.png', triggered=self.show_menu) diff --git a/src/calibre/gui2/actions/catalog.py b/src/calibre/gui2/actions/catalog.py index 536dc684d6..4ffe9e9923 100644 --- a/src/calibre/gui2/actions/catalog.py +++ b/src/calibre/gui2/actions/catalog.py @@ -73,10 +73,10 @@ class GenerateCatalogAction(InterfaceAction): # Subsequent strings are error messages dialog_title = job.result.pop(0) if re.search('warning', job.result[0].lower()): - msg = _("Catalog generation complete, with warnings.") + msg = _('Catalog generation complete, with warnings.') warning_dialog(self.gui, dialog_title, msg, det_msg='\n'.join(job.result), show=True) else: - job.result.append("Catalog generation terminated.") + job.result.append('Catalog generation terminated.') error_dialog(self.gui, dialog_title,'\n'.join(job.result),show=True) return diff --git a/src/calibre/gui2/actions/delete.py b/src/calibre/gui2/actions/delete.py index 538af5a3aa..b933bd6607 100644 --- a/src/calibre/gui2/actions/delete.py +++ b/src/calibre/gui2/actions/delete.py @@ -438,7 +438,7 @@ class DeleteAction(InterfaceActionWithLibraryDrop): try: view.model().delete_books_by_id(to_delete_ids) except OSError as err: - err.locking_violation_msg = _('Could not change on-disk location of this book\'s files.') + err.locking_violation_msg = _("Could not change on-disk location of this book's files.") raise self.library_ids_deleted2(to_delete_ids, next_id=next_id, can_undo=True) else: diff --git a/src/calibre/gui2/actions/device.py b/src/calibre/gui2/actions/device.py index 43d59a4a26..1db7bd37b9 100644 --- a/src/calibre/gui2/actions/device.py +++ b/src/calibre/gui2/actions/device.py @@ -59,7 +59,7 @@ class ShareConnMenu(QMenu): # {{{ _('Start Content server')) connect_lambda(self.toggle_server_action.triggered, self, lambda self: self.toggle_server.emit()) self.open_server_in_browser_action = self.addAction( - QIcon.ic('forward.png'), _("Visit Content server in browser")) + QIcon.ic('forward.png'), _('Visit Content server in browser')) connect_lambda(self.open_server_in_browser_action.triggered, self, lambda self: open_in_browser()) self.open_server_in_browser_action.setVisible(False) self.control_smartdevice_action = \ diff --git a/src/calibre/gui2/actions/layout_actions.py b/src/calibre/gui2/actions/layout_actions.py index a6316d7845..3ec18e573c 100644 --- a/src/calibre/gui2/actions/layout_actions.py +++ b/src/calibre/gui2/actions/layout_actions.py @@ -86,8 +86,8 @@ class LayoutActions(InterfaceAction): menu=self.hidden_menu, unique_name='Main window layout', shortcut=None, - text=_("Save and restore layout item sizes, and add/remove/toggle " - "layout items such as the search bar, tag browser, etc. "), + text=_('Save and restore layout item sizes, and add/remove/toggle ' + 'layout items such as the search bar, tag browser, etc. '), icon='layout.png', triggered=self.show_menu) diff --git a/src/calibre/gui2/actions/open.py b/src/calibre/gui2/actions/open.py index 8998e5f343..2d3c5f6c41 100644 --- a/src/calibre/gui2/actions/open.py +++ b/src/calibre/gui2/actions/open.py @@ -14,7 +14,7 @@ class OpenFolderAction(InterfaceAction): name = 'Open Folder' action_spec = (_('Open book folder'), 'document_open.png', - _('Open the folder containing the current book\'s files'), _('O')) + _("Open the folder containing the current book's files"), _('O')) dont_add_to = frozenset(('context-menu-device',)) action_type = 'current' action_add_menu = True diff --git a/src/calibre/gui2/actions/save_to_disk.py b/src/calibre/gui2/actions/save_to_disk.py index 39bddad3e3..6c183941bf 100644 --- a/src/calibre/gui2/actions/save_to_disk.py +++ b/src/calibre/gui2/actions/save_to_disk.py @@ -17,7 +17,7 @@ from polyglot.builtins import itervalues class SaveToDiskAction(InterfaceAction): - name = "Save To Disk" + name = 'Save To Disk' action_spec = (_('Save to disk'), 'save.png', _('Export e-book files from the calibre library'), _('S')) action_type = 'current' diff --git a/src/calibre/gui2/bars.py b/src/calibre/gui2/bars.py index 44f063bc45..fc86b10b7e 100644 --- a/src/calibre/gui2/bars.py +++ b/src/calibre/gui2/bars.py @@ -273,8 +273,8 @@ class ToolBar(QToolBar): # {{{ def dragEnterEvent(self, event): md = event.mimeData() - if md.hasFormat("application/calibre+from_library") or \ - md.hasFormat("application/calibre+from_device"): + if md.hasFormat('application/calibre+from_library') or \ + md.hasFormat('application/calibre+from_device'): event.setDropAction(Qt.DropAction.CopyAction) event.accept() return @@ -292,8 +292,8 @@ class ToolBar(QToolBar): # {{{ w = self.widgetForAction(ac) if w is not None: if (md.hasFormat( - "application/calibre+from_library") or md.hasFormat( - "application/calibre+from_device")) and \ + 'application/calibre+from_library') or md.hasFormat( + 'application/calibre+from_device')) and \ w.geometry().contains(event.pos()) and \ isinstance(w, QToolButton) and not w.isChecked(): allowed = True diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py index b734967088..0ea421b5b9 100644 --- a/src/calibre/gui2/book_details.py +++ b/src/calibre/gui2/book_details.py @@ -524,7 +524,7 @@ def add_item_specific_entries(menu, data, book_info, copy_menu, search_menu): menu.addAction(ac) # See if we need to add a click associated link menu line for the author link_map = get_gui().current_db.new_api.get_all_link_maps_for_book(data.get('book_id', -1)) - link = link_map.get("authors", {}).get(author) + link = link_map.get('authors', {}).get(author) if link: add_link_submenu(menu, link, book_info, 'authors', author) elif dt in ('path', 'devpath'): @@ -542,7 +542,7 @@ def add_item_specific_entries(menu, data, book_info, copy_menu, search_menu): if path: path = os.path.join(path, DATA_DIR_NAME) ac.current_url = path - ac.setText(_('The location of the book\'s data files')) + ac.setText(_("The location of the book's data files")) copy_menu.addAction(ac) else: field = data.get('field') @@ -643,7 +643,7 @@ def create_copy_links(menu, data=None): note_data['searchable_text'].partition('\n')[2], sep) if field.startswith('#'): field = '_' + field[1:] - url = f"calibre://show-note/{library_id}/{field}/id_{item_id}" + url = f'calibre://show-note/{library_id}/{field}/id_{item_id}' link_action(_('Link to show note in calibre'), url) else: field = data.get('field') diff --git a/src/calibre/gui2/catalog/catalog_csv_xml.py b/src/calibre/gui2/catalog/catalog_csv_xml.py index efb94c6c0c..320eb006d5 100644 --- a/src/calibre/gui2/catalog/catalog_csv_xml.py +++ b/src/calibre/gui2/catalog/catalog_csv_xml.py @@ -68,7 +68,7 @@ class PluginWidget(QWidget): self.db_fields.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove) self.db_fields.setDefaultDropAction(Qt.DropAction.CopyAction if ismacos else Qt.DropAction.MoveAction) self.db_fields.setAlternatingRowColors(True) - self.db_fields.setObjectName("db_fields") + self.db_fields.setObjectName('db_fields') h = QHBoxLayout() l.addLayout(h) h.addWidget(la), h.addStretch(10) diff --git a/src/calibre/gui2/catalog/catalog_epub_mobi.py b/src/calibre/gui2/catalog/catalog_epub_mobi.py index 16172cbb97..f9cd0a2b00 100644 --- a/src/calibre/gui2/catalog/catalog_epub_mobi.py +++ b/src/calibre/gui2/catalog/catalog_epub_mobi.py @@ -134,7 +134,7 @@ class PluginWidget(QWidget,Ui_Form): def block_all_signals(self, bool): if self.DEBUG: - print("block_all_signals: %s" % bool) + print('block_all_signals: %s' % bool) self.blocking_all_signals = bool for opt in self.OPTION_FIELDS: c_name, c_def, c_type = opt @@ -176,7 +176,7 @@ class PluginWidget(QWidget,Ui_Form): opts_dict[c_name[:-3]] = opt_value def exclude_genre_changed(self): - """ Dynamically compute excluded genres. + ''' Dynamically compute excluded genres. Run exclude_genre regex against selected genre_source_field to show excluded tags. @@ -186,7 +186,7 @@ class PluginWidget(QWidget,Ui_Form): Output: self.exclude_genre_results (QLabel): updated to show tags to be excluded as genres - """ + ''' def _truncated_results(excluded_tags, limit=180): ''' Limit number of genres displayed to avoid dialog explosion @@ -211,7 +211,7 @@ class PluginWidget(QWidget,Ui_Form): if excluded_tags == start + end: return ', '.join(excluded_tags) else: - return "{} ... {}".format(', '.join(start), ', '.join(end)) + return '{} ... {}'.format(', '.join(start), ', '.join(end)) results = _('No genres will be excluded') @@ -230,7 +230,7 @@ class PluginWidget(QWidget,Ui_Form): try: pattern = re.compile(regex) except: - results = _("regex error: %s") % sys.exc_info()[1] + results = _('regex error: %s') % sys.exc_info()[1] else: excluded_tags = [] for tag in all_genre_tags: @@ -239,12 +239,12 @@ class PluginWidget(QWidget,Ui_Form): excluded_tags.append(hit.string) if excluded_tags: if set(excluded_tags) == set(all_genre_tags): - results = _("All genres will be excluded") + results = _('All genres will be excluded') else: results = _truncated_results(excluded_tags) finally: if False and self.DEBUG: - print("exclude_genre_changed(): %s" % results) + print('exclude_genre_changed(): %s' % results) self.exclude_genre_results.clear() self.exclude_genre_results.setText(results) @@ -423,11 +423,11 @@ class PluginWidget(QWidget,Ui_Form): # Initialize exclusion rules self.exclusion_rules_table = ExclusionRules(self, self.exclusion_rules_gb, - "exclusion_rules_tw", exclusion_rules) + 'exclusion_rules_tw', exclusion_rules) # Initialize prefix rules self.prefix_rules_table = PrefixRules(self, self.prefix_rules_gb, - "prefix_rules_tw", prefix_rules) + 'prefix_rules_tw', prefix_rules) # Initialize excluded genres preview self.exclude_genre_changed() @@ -532,7 +532,7 @@ class PluginWidget(QWidget,Ui_Form): genre_source_spec = self.genre_source_fields[cs] self.genre_source_field_name = genre_source_spec['field'] - opts_dict['merge_comments_rule'] = "%s:%s:%s" % \ + opts_dict['merge_comments_rule'] = '%s:%s:%s' % \ (self.merge_source_field_name, checked, include_hr) opts_dict['header_note_source_field'] = self.header_note_source_field_name @@ -550,9 +550,9 @@ class PluginWidget(QWidget,Ui_Form): opts_dict['output_profile'] = ['default'] if False and self.DEBUG: - print("opts_dict") + print('opts_dict') for opt in sorted(opts_dict.keys(), key=sort_key): - print(f" {opt}: {repr(opts_dict[opt])}") + print(f' {opt}: {repr(opts_dict[opt])}') return opts_dict def populate_combo_boxes(self): @@ -625,8 +625,8 @@ class PluginWidget(QWidget,Ui_Form): self.genre_source_field.currentIndexChanged.connect(self.genre_source_field_changed) # Populate the Presets combo box - self.presets = JSONConfig("catalog_presets") - self.preset_field.addItem("") + self.presets = JSONConfig('catalog_presets') + self.preset_field.addItem('') self.preset_field_values = sorted(self.presets, key=sort_key) self.preset_field.addItems(self.preset_field_values) @@ -687,12 +687,12 @@ class PluginWidget(QWidget,Ui_Form): # Reset exclusion rules self.exclusion_rules_table.clearLayout() self.exclusion_rules_table = ExclusionRules(self, self.exclusion_rules_gb, - "exclusion_rules_tw", exclusion_rules) + 'exclusion_rules_tw', exclusion_rules) # Reset prefix rules self.prefix_rules_table.clearLayout() self.prefix_rules_table = PrefixRules(self, self.prefix_rules_gb, - "prefix_rules_tw", prefix_rules) + 'prefix_rules_tw', prefix_rules) # Reset excluded genres preview self.exclude_genre_changed() @@ -711,9 +711,9 @@ class PluginWidget(QWidget,Ui_Form): if self.preset_field.currentIndex() == 0: return - if not question_dialog(self, _("Delete saved catalog preset"), - _("The selected saved catalog preset will be deleted. " - "Are you sure?")): + if not question_dialog(self, _('Delete saved catalog preset'), + _('The selected saved catalog preset will be deleted. ' + 'Are you sure?')): return item_id = self.preset_field.currentIndex() @@ -742,14 +742,14 @@ class PluginWidget(QWidget,Ui_Form): if not ok: return if not name: - error_dialog(self, _("Save catalog preset"), - _("You must provide a name."), show=True) + error_dialog(self, _('Save catalog preset'), + _('You must provide a name.'), show=True) new = True name = str(name) if name in self.presets.keys(): - if not question_dialog(self, _("Save catalog preset"), - _("That saved preset already exists and will be overwritten. " - "Are you sure?")): + if not question_dialog(self, _('Save catalog preset'), + _('That saved preset already exists and will be overwritten. ' + 'Are you sure?')): return new = False @@ -799,7 +799,7 @@ class PluginWidget(QWidget,Ui_Form): elif self.merge_after.isChecked(): checked = 'after' include_hr = self.include_hr.isChecked() - preset['merge_comments_rule'] = "%s:%s:%s" % \ + preset['merge_comments_rule'] = '%s:%s:%s' % \ (self.merge_source_field_name, checked, include_hr) preset['header_note_source_field'] = str(self.header_note_source_field.currentText()) @@ -839,7 +839,7 @@ class PluginWidget(QWidget,Ui_Form): When anything changes, clear Preset combobox ''' if self.DEBUG: - print("settings_changed: %s" % source) + print('settings_changed: %s' % source) self.preset_field.setCurrentIndex(0) def show_help(self): @@ -947,28 +947,28 @@ class GenericRulesTable(QTableWidget): # Add the control set vbl = QVBoxLayout() self.move_rule_up_tb = QToolButton() - self.move_rule_up_tb.setObjectName("move_rule_up_tb") + self.move_rule_up_tb.setObjectName('move_rule_up_tb') self.move_rule_up_tb.setToolTip('Move rule up') self.move_rule_up_tb.setIcon(QIcon.ic('arrow-up.png')) self.move_rule_up_tb.clicked.connect(self.move_row_up) vbl.addWidget(self.move_rule_up_tb) self.add_rule_tb = QToolButton() - self.add_rule_tb.setObjectName("add_rule_tb") + self.add_rule_tb.setObjectName('add_rule_tb') self.add_rule_tb.setToolTip('Add a new rule') self.add_rule_tb.setIcon(QIcon.ic('plus.png')) self.add_rule_tb.clicked.connect(self.add_row) vbl.addWidget(self.add_rule_tb) self.delete_rule_tb = QToolButton() - self.delete_rule_tb.setObjectName("delete_rule_tb") + self.delete_rule_tb.setObjectName('delete_rule_tb') self.delete_rule_tb.setToolTip('Delete selected rule') self.delete_rule_tb.setIcon(QIcon.ic('list_remove.png')) self.delete_rule_tb.clicked.connect(self.delete_row) vbl.addWidget(self.delete_rule_tb) self.move_rule_down_tb = QToolButton() - self.move_rule_down_tb.setObjectName("move_rule_down_tb") + self.move_rule_down_tb.setObjectName('move_rule_down_tb') self.move_rule_down_tb.setToolTip('Move rule down') self.move_rule_down_tb.setIcon(QIcon.ic('arrow-down.png')) self.move_rule_down_tb.clicked.connect(self.move_row_down) @@ -980,7 +980,7 @@ class GenericRulesTable(QTableWidget): self.setFocus() row = self.last_row_selected + 1 if self.DEBUG: - print("%s:add_row(): at row: %d" % (self.objectName(), row)) + print('%s:add_row(): at row: %d' % (self.objectName(), row)) self.insertRow(row) self.populate_table_row(row, self.create_blank_row_data()) self.select_and_scroll_to_row(row) @@ -1004,7 +1004,7 @@ class GenericRulesTable(QTableWidget): def delete_row(self): if self.DEBUG: - print("%s:delete_row()" % self.objectName()) + print('%s:delete_row()' % self.objectName()) self.setFocus() rows = self.last_rows_selected @@ -1031,14 +1031,14 @@ class GenericRulesTable(QTableWidget): def enabled_state_changed(self, row, col): if col in [self.COLUMNS['ENABLED']['ordinal']]: self.select_and_scroll_to_row(row) - self.settings_changed("enabled_state_changed") + self.settings_changed('enabled_state_changed') if self.DEBUG: - print("%s:enabled_state_changed(): row %d col %d" % + print('%s:enabled_state_changed(): row %d col %d' % (self.objectName(), row, col)) def focusInEvent(self,e): if self.DEBUG: - print("%s:focusInEvent()" % self.objectName()) + print('%s:focusInEvent()' % self.objectName()) def focusOutEvent(self,e): # Override of QTableWidget method - clear selection when table loses focus @@ -1046,7 +1046,7 @@ class GenericRulesTable(QTableWidget): self.last_rows_selected = self.selectionModel().selectedRows() self.clearSelection() if self.DEBUG: - print("%s:focusOutEvent(): self.last_row_selected: %d" % (self.objectName(),self.last_row_selected)) + print('%s:focusOutEvent(): self.last_row_selected: %d' % (self.objectName(),self.last_row_selected)) def move_row_down(self): self.setFocus() @@ -1062,7 +1062,7 @@ class GenericRulesTable(QTableWidget): dest_row = selrow.row() + 1 src_row = selrow.row() if self.DEBUG: - print("%s:move_row_down() %d -> %d" % (self.objectName(),src_row, dest_row)) + print('%s:move_row_down() %d -> %d' % (self.objectName(),src_row, dest_row)) # Save the contents of the destination row saved_data = self.convert_row_to_data(dest_row) @@ -1092,7 +1092,7 @@ class GenericRulesTable(QTableWidget): for selrow in rows: if self.DEBUG: - print("%s:move_row_up() %d -> %d" % (self.objectName(),selrow.row(), selrow.row()-1)) + print('%s:move_row_up() %d -> %d' % (self.objectName(),selrow.row(), selrow.row()-1)) # Save the row above saved_data = self.convert_row_to_data(selrow.row() - 1) @@ -1129,12 +1129,12 @@ class GenericRulesTable(QTableWidget): def rule_name_edited(self): if self.DEBUG: - print("%s:rule_name_edited()" % self.objectName()) + print('%s:rule_name_edited()' % self.objectName()) current_row = self.currentRow() self.cellWidget(current_row,1).home(False) self.select_and_scroll_to_row(current_row) - self.settings_changed("rule_name_edited") + self.settings_changed('rule_name_edited') def select_and_scroll_to_row(self, row): self.setFocus() @@ -1154,7 +1154,7 @@ class GenericRulesTable(QTableWidget): break if self.DEBUG: - print("%s:_source_index_changed(): calling source_index_changed with row: %d " % + print('%s:_source_index_changed(): calling source_index_changed with row: %d ' % (self.objectName(), row)) self.source_index_changed(combo, row) @@ -1184,18 +1184,18 @@ class GenericRulesTable(QTableWidget): values_combo.currentIndexChanged.connect(partial(self.values_index_changed, values_combo)) self.setCellWidget(row, self.COLUMNS['PATTERN']['ordinal'], values_combo) self.select_and_scroll_to_row(row) - self.settings_changed("source_index_changed") + self.settings_changed('source_index_changed') def values_index_changed(self, combo): # After edit, select row for row in range(self.rowCount()): if self.cellWidget(row, self.COLUMNS['PATTERN']['ordinal']) is combo: self.select_and_scroll_to_row(row) - self.settings_changed("values_index_changed") + self.settings_changed('values_index_changed') break if self.DEBUG: - print("%s:values_index_changed(): row %d " % + print('%s:values_index_changed(): row %d ' % (self.objectName(), row)) @@ -1208,7 +1208,7 @@ class ExclusionRules(GenericRulesTable): def __init__(self, parent, parent_gb_hl, object_name, rules): super().__init__(parent, parent_gb_hl, object_name, rules) - self.setObjectName("exclusion_rules_table") + self.setObjectName('exclusion_rules_table') self._init_table_widget() self._initialize() @@ -1301,7 +1301,7 @@ class PrefixRules(GenericRulesTable): def __init__(self, parent, parent_gb_hl, object_name, rules): super().__init__(parent, parent_gb_hl, object_name, rules) - self.setObjectName("prefix_rules_table") + self.setObjectName('prefix_rules_table') self._init_table_widget() self._initialize() @@ -1344,7 +1344,7 @@ class PrefixRules(GenericRulesTable): def generate_prefix_list(self): def prefix_sorter(item): key = item - if item[0] == "_": + if item[0] == '_': key = 'zzz' + item return key diff --git a/src/calibre/gui2/comments_editor.py b/src/calibre/gui2/comments_editor.py index bf8413951c..015ed28e18 100644 --- a/src/calibre/gui2/comments_editor.py +++ b/src/calibre/gui2/comments_editor.py @@ -1241,7 +1241,7 @@ class Highlighter(QSyntaxHighlighter): if state == State_Comment: start = pos while pos < len_: - if text[pos:pos+3] == "-->": + if text[pos:pos+3] == '-->': pos += 3 state = State_Text break @@ -1398,10 +1398,10 @@ class Highlighter(QSyntaxHighlighter): while pos < len_: ch = text[pos] if ch == '<': - if text[pos:pos+4] == "<!--": + if text[pos:pos+4] == '<!--': state = State_Comment else: - if text[pos:pos+9].upper() == "<!DOCTYPE": + if text[pos:pos+9].upper() == '<!DOCTYPE': state = State_DocType else: state = State_TagStart diff --git a/src/calibre/gui2/convert/__init__.py b/src/calibre/gui2/convert/__init__.py index 1194417ae2..9b5e2836f2 100644 --- a/src/calibre/gui2/convert/__init__.py +++ b/src/calibre/gui2/convert/__init__.py @@ -194,7 +194,7 @@ class Widget(QWidget): elif isinstance(g, RegexEdit): return g.regex if g.regex else None else: - raise Exception('Can\'t get value from %s'%type(g)) + raise Exception("Can't get value from %s"%type(g)) def gui_obj_changed(self, gui_obj, *args): self.changed_signal.emit() @@ -223,7 +223,7 @@ class Widget(QWidget): elif isinstance(g, FontFamilyChooser): g.family_changed.connect(f) else: - raise Exception('Can\'t connect %s'%type(g)) + raise Exception("Can't connect %s"%type(g)) def connect_gui_obj_handler(self, gui_obj, slot): raise NotImplementedError() @@ -264,7 +264,7 @@ class Widget(QWidget): elif isinstance(g, (XPathEdit, RegexEdit)): g.edit.setText(val if val else '') else: - raise Exception('Can\'t set value %s in %s'%(repr(val), + raise Exception("Can't set value %s in %s"%(repr(val), str(g.objectName()))) self.post_set_value(g, val) diff --git a/src/calibre/gui2/convert/gui_conversion.py b/src/calibre/gui2/convert/gui_conversion.py index 641af19e33..e51619048d 100644 --- a/src/calibre/gui2/convert/gui_conversion.py +++ b/src/calibre/gui2/convert/gui_conversion.py @@ -53,7 +53,7 @@ def gui_catalog(fmt, title, dbspec, ids, out_file_name, sync, fmt_options, conne # Create a minimal OptionParser that we can append to parser = OptionParser() args = [] - parser.add_option("--verbose", action="store_true", dest="verbose", default=True) + parser.add_option('--verbose', action='store_true', dest='verbose', default=True) opts, args = parser.parse_args() # Populate opts diff --git a/src/calibre/gui2/convert/metadata.py b/src/calibre/gui2/convert/metadata.py index a6f587f255..cb665228c8 100644 --- a/src/calibre/gui2/convert/metadata.py +++ b/src/calibre/gui2/convert/metadata.py @@ -200,11 +200,11 @@ class MetadataWidget(Widget, Ui_Form): return cover = None try: - with open(_file, "rb") as f: + with open(_file, 'rb') as f: cover = f.read() except OSError as e: d = error_dialog(self.parent(), _('Error reading file'), - _("<p>There was an error reading from file: <br /><b>") + _file + "</b></p><br />"+str(e)) + _('<p>There was an error reading from file: <br /><b>') + _file + '</b></p><br />'+str(e)) d.exec() if cover: pix = QPixmap() @@ -212,7 +212,7 @@ class MetadataWidget(Widget, Ui_Form): pix.setDevicePixelRatio(getattr(self, 'devicePixelRatioF', self.devicePixelRatio)()) if pix.isNull(): d = error_dialog(self.parent(), _('Error reading file'), - _file + _(" is not a valid picture")) + _file + _(' is not a valid picture')) d.exec() else: self.cover_path.setText(_file) @@ -243,7 +243,7 @@ class MetadataWidget(Widget, Ui_Form): if self.cover_changed and self.cover_data is not None: self.db.set_cover(self.book_id, self.cover_data) except OSError as err: - err.locking_violation_msg = _('Failed to change on disk location of this book\'s files.') + err.locking_violation_msg = _("Failed to change on disk location of this book's files.") raise publisher = self.publisher.text().strip() if publisher != db.field_for('publisher', self.book_id): diff --git a/src/calibre/gui2/convert/single.py b/src/calibre/gui2/convert/single.py index 8587ba95bd..4947fea16f 100644 --- a/src/calibre/gui2/convert/single.py +++ b/src/calibre/gui2/convert/single.py @@ -114,33 +114,33 @@ class Config(QDialog): self.show_pane(cur) def setupUi(self): - self.setObjectName("Dialog") + self.setObjectName('Dialog') self.resize(1024, 700) self.setWindowIcon(QIcon.ic('convert.png')) self.gridLayout = QGridLayout(self) - self.gridLayout.setObjectName("gridLayout") + self.gridLayout.setObjectName('gridLayout') self.horizontalLayout = QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") + self.horizontalLayout.setObjectName('horizontalLayout') self.input_label = QLabel(self) - self.input_label.setObjectName("input_label") + self.input_label.setObjectName('input_label') self.horizontalLayout.addWidget(self.input_label) self.input_formats = QComboBox(self) self.input_formats.setSizeAdjustPolicy(QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon) self.input_formats.setMinimumContentsLength(5) - self.input_formats.setObjectName("input_formats") + self.input_formats.setObjectName('input_formats') self.horizontalLayout.addWidget(self.input_formats) self.opt_individual_saved_settings = QCheckBox(self) - self.opt_individual_saved_settings.setObjectName("opt_individual_saved_settings") + self.opt_individual_saved_settings.setObjectName('opt_individual_saved_settings') self.horizontalLayout.addWidget(self.opt_individual_saved_settings) spacerItem = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout.addItem(spacerItem) self.label_2 = QLabel(self) - self.label_2.setObjectName("label_2") + self.label_2.setObjectName('label_2') self.horizontalLayout.addWidget(self.label_2) self.output_formats = QComboBox(self) self.output_formats.setSizeAdjustPolicy(QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon) self.output_formats.setMinimumContentsLength(5) - self.output_formats.setObjectName("output_formats") + self.output_formats.setObjectName('output_formats') self.horizontalLayout.addWidget(self.output_formats) self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 2) self.groups = QListView(self) @@ -152,7 +152,7 @@ class Config(QDialog): self.groups.setTabKeyNavigation(True) self.groups.setIconSize(QSize(48, 48)) self.groups.setWordWrap(True) - self.groups.setObjectName("groups") + self.groups.setObjectName('groups') self.gridLayout.addWidget(self.groups, 1, 0, 3, 1) self.scrollArea = QScrollArea(self) sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) @@ -163,16 +163,16 @@ class Config(QDialog): self.scrollArea.setFrameShape(QFrame.Shape.NoFrame) self.scrollArea.setLineWidth(0) self.scrollArea.setWidgetResizable(True) - self.scrollArea.setObjectName("scrollArea") + self.scrollArea.setObjectName('scrollArea') self.page = QWidget() - self.page.setObjectName("page") + self.page.setObjectName('page') self.gridLayout.addWidget(self.scrollArea, 1, 1, 1, 1) self.buttonBox = QDialogButtonBox(self) self.buttonBox.setOrientation(Qt.Orientation.Horizontal) self.buttonBox.setStandardButtons( QDialogButtonBox.StandardButton.Cancel|QDialogButtonBox.StandardButton.Ok| QDialogButtonBox.StandardButton.RestoreDefaults) - self.buttonBox.setObjectName("buttonBox") + self.buttonBox.setObjectName('buttonBox') self.gridLayout.addWidget(self.buttonBox, 3, 1, 1, 1) self.help = QTextEdit(self) self.help.setReadOnly(True) @@ -182,13 +182,13 @@ class Config(QDialog): sizePolicy.setHeightForWidth(self.help.sizePolicy().hasHeightForWidth()) self.help.setSizePolicy(sizePolicy) self.help.setMaximumHeight(80) - self.help.setObjectName("help") + self.help.setObjectName('help') self.gridLayout.addWidget(self.help, 2, 1, 1, 1) self.input_label.setBuddy(self.input_formats) self.label_2.setBuddy(self.output_formats) - self.input_label.setText(_("&Input format:")) - self.opt_individual_saved_settings.setText(_("Use &saved conversion settings for individual books")) - self.label_2.setText(_("&Output format:")) + self.input_label.setText(_('&Input format:')) + self.opt_individual_saved_settings.setText(_('Use &saved conversion settings for individual books')) + self.label_2.setText(_('&Output format:')) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index b61e3c24ca..0fb477ab6a 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -1327,12 +1327,12 @@ class BulkSeries(BulkBase): self.series_start_number = QDoubleSpinBox(parent) self.series_start_number.setMinimum(0.0) self.series_start_number.setMaximum(9999999.0) - self.series_start_number.setProperty("value", 1.0) + self.series_start_number.setProperty('value', 1.0) layout.addWidget(self.series_start_number) self.series_increment = QDoubleSpinBox(parent) self.series_increment.setMinimum(0.00) self.series_increment.setMaximum(99999.0) - self.series_increment.setProperty("value", 1.0) + self.series_increment.setProperty('value', 1.0) self.series_increment.setToolTip('<p>' + _( 'The amount by which to increment the series number ' 'for successive books. Only applicable when using ' diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index d8777e430a..9a3119b3de 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -1530,7 +1530,7 @@ class DeviceMixin: # {{{ pass total_size = self.location_manager.free[0] loc = tweaks['send_news_to_device_location'] - loc_index = {"carda": 1, "cardb": 2}.get(loc, 0) + loc_index = {'carda': 1, 'cardb': 2}.get(loc, 0) if self.location_manager.free[loc_index] > total_size + (1024**2): # Send news to main memory if enough space available # as some devices like the Nook Color cannot handle diff --git a/src/calibre/gui2/device_drivers/tabbed_device_config.py b/src/calibre/gui2/device_drivers/tabbed_device_config.py index 8df831ccb6..76c7cacc5c 100644 --- a/src/calibre/gui2/device_drivers/tabbed_device_config.py +++ b/src/calibre/gui2/device_drivers/tabbed_device_config.py @@ -44,7 +44,7 @@ def create_checkbox(title, tt, state): class TabbedDeviceConfig(QTabWidget): - """ + ''' This is a generic Tabbed Device config widget. It designed for devices with more complex configuration. But, it is backwards compatible to the standard device configuration widget. @@ -63,7 +63,7 @@ class TabbedDeviceConfig(QTabWidget): DeviceConfigTab, for each set of options. Within the tabs, group boxes, subclassed from DeviceOptionsGroupBox, are created to further group the options. The group boxes can be coded to support any control type and dependencies between them. - """ + ''' def __init__(self, device_settings, all_formats, supports_subdirs, must_read_metadata, supports_use_author_sort, @@ -90,7 +90,7 @@ class TabbedDeviceConfig(QTabWidget): self.base = QWidget(self) # self.insertTab(0, self.base, _('Configure %s') % self.device.current_friendly_name) - self.insertTab(0, self.base, _("File formats")) + self.insertTab(0, self.base, _('File formats')) l = self.base.l = QGridLayout(self.base) self.base.setLayout(l) @@ -99,23 +99,23 @@ class TabbedDeviceConfig(QTabWidget): self.formats.hide() self.opt_use_subdirs = create_checkbox( - _("Use sub-folders"), + _('Use sub-folders'), _('Place files in sub-folders if the device supports them'), device_settings.use_subdirs ) self.opt_read_metadata = create_checkbox( - _("Read metadata from files on device"), + _('Read metadata from files on device'), _('Read metadata from files on device'), device_settings.read_metadata ) self.template = TemplateConfig(device_settings.save_template) self.opt_use_author_sort = create_checkbox( - _("Use author sort for author"), - _("Use author sort for author"), + _('Use author sort for author'), + _('Use author sort for author'), device_settings.read_metadata ) - self.opt_use_author_sort.setObjectName("opt_use_author_sort") + self.opt_use_author_sort.setObjectName('opt_use_author_sort') self.base.la = la = QLabel(_( 'Choose the formats to send to the %s')%self.device_name) la.setWordWrap(True) @@ -216,7 +216,7 @@ class TabbedDeviceConfig(QTabWidget): return True def commit(self): - debug_print("TabbedDeviceConfig::commit: start") + debug_print('TabbedDeviceConfig::commit: start') p = self.device._configProxy() p['format_map'] = self.formats.format_map @@ -261,19 +261,19 @@ class ExtraCustomization(DeviceConfigTab): # {{{ def __init__(self, extra_customization_message, extra_customization_choices, device_settings): super().__init__() - debug_print("ExtraCustomization.__init__ - extra_customization_message=", extra_customization_message) - debug_print("ExtraCustomization.__init__ - extra_customization_choices=", extra_customization_choices) - debug_print("ExtraCustomization.__init__ - device_settings.extra_customization=", device_settings.extra_customization) - debug_print("ExtraCustomization.__init__ - device_settings=", device_settings) + debug_print('ExtraCustomization.__init__ - extra_customization_message=', extra_customization_message) + debug_print('ExtraCustomization.__init__ - extra_customization_choices=', extra_customization_choices) + debug_print('ExtraCustomization.__init__ - device_settings.extra_customization=', device_settings.extra_customization) + debug_print('ExtraCustomization.__init__ - device_settings=', device_settings) self.extra_customization_message = extra_customization_message self.l = QVBoxLayout(self) self.setLayout(self.l) - options_group = QGroupBox(_("Extra driver customization options"), self) + options_group = QGroupBox(_('Extra driver customization options'), self) self.l.addWidget(options_group) self.extra_layout = QGridLayout() - self.extra_layout.setObjectName("extra_layout") + self.extra_layout.setObjectName('extra_layout') options_group.setLayout(self.extra_layout) if extra_customization_message: @@ -365,18 +365,18 @@ class ExtraCustomization(DeviceConfigTab): # {{{ @property def has_extra_customizations(self): - debug_print("ExtraCustomization::has_extra_customizations - self.extra_customization_message", self.extra_customization_message) + debug_print('ExtraCustomization::has_extra_customizations - self.extra_customization_message', self.extra_customization_message) return self.extra_customization_message and len(self.extra_customization_message) > 0 # }}} class DeviceOptionsGroupBox(QGroupBox): - """ + ''' This is a container for the individual options for a device driver. - """ + ''' - def __init__(self, parent, device=None, title=_("Unknown")): + def __init__(self, parent, device=None, title=_('Unknown')): QGroupBox.__init__(self, parent) self.device = device @@ -391,7 +391,7 @@ if __name__ == '__main__': s.scan() app = Application([]) dev = KOBO(None) - debug_print("KOBO:", KOBO) + debug_print('KOBO:', KOBO) # dev.startup() # cd = dev.detect_managed_devices(s.devices) # dev.open(cd, 'test') diff --git a/src/calibre/gui2/dialogs/add_from_isbn.py b/src/calibre/gui2/dialogs/add_from_isbn.py index 7bc1b99921..17c48e53c4 100644 --- a/src/calibre/gui2/dialogs/add_from_isbn.py +++ b/src/calibre/gui2/dialogs/add_from_isbn.py @@ -30,7 +30,7 @@ class AddFromISBN(QDialog): def setup_ui(self): self.resize(678, 430) - self.setWindowTitle(_("Add books by ISBN")) + self.setWindowTitle(_('Add books by ISBN')) self.setWindowIcon(QIcon.ic('add_book.png')) self.l = l = QVBoxLayout(self) self.h = h = QHBoxLayout() @@ -43,24 +43,24 @@ class AddFromISBN(QDialog): self.isbn_box = i = QPlainTextEdit(self) i.setFocus(Qt.FocusReason.OtherFocusReason) l.addWidget(i) - self.paste_button = b = QPushButton(_("&Paste from clipboard"), self) + self.paste_button = b = QPushButton(_('&Paste from clipboard'), self) l.addWidget(b), b.clicked.connect(self.paste) self.lll = l = QVBoxLayout() h.addLayout(l) self.label = la = QLabel(_( - "<p>Enter a list of ISBNs in the box to the left, one per line. calibre will automatically" - " create entries for books based on the ISBN and download metadata and covers for them.</p>\n" - "<p>Any invalid ISBNs in the list will be ignored.</p>\n" - "<p>You can also specify a file that will be added with each ISBN. To do this enter the full" - " path to the file after a <code>>></code>. For example:</p>\n" - "<p><code>9788842915232 >> %s</code></p>" - "<p>To use identifiers other than ISBN use key:value syntax, For example:</p>\n" - "<p><code>amazon:B001JK9C72</code></p>" + '<p>Enter a list of ISBNs in the box to the left, one per line. calibre will automatically' + ' create entries for books based on the ISBN and download metadata and covers for them.</p>\n' + '<p>Any invalid ISBNs in the list will be ignored.</p>\n' + '<p>You can also specify a file that will be added with each ISBN. To do this enter the full' + ' path to the file after a <code>>></code>. For example:</p>\n' + '<p><code>9788842915232 >> %s</code></p>' + '<p>To use identifiers other than ISBN use key:value syntax, For example:</p>\n' + '<p><code>amazon:B001JK9C72</code></p>' ), self) l.addWidget(la), la.setWordWrap(True) l.addSpacing(20) - self.la2 = la = QLabel(_("&Tags to set on created book entries:"), self) + self.la2 = la = QLabel(_('&Tags to set on created book entries:'), self) l.addWidget(la) self.add_tags = le = QLineEdit(self) le.setText(', '.join(gprefs.get('add from ISBN tags', []))) diff --git a/src/calibre/gui2/dialogs/catalog.py b/src/calibre/gui2/dialogs/catalog.py index acc3b144ae..9c5909e012 100644 --- a/src/calibre/gui2/dialogs/catalog.py +++ b/src/calibre/gui2/dialogs/catalog.py @@ -57,7 +57,7 @@ class Catalog(QDialog, Ui_Dialog): self.widgets.append(pw) [self.fmts.append([file_type.upper(), pw.sync_enabled,pw]) for file_type in plugin.file_types] except ImportError: - info("ImportError initializing %s" % name) + info('ImportError initializing %s' % name) continue else: # Load dynamic tab @@ -90,13 +90,13 @@ class Catalog(QDialog, Ui_Dialog): self.widgets.append(pw) [self.fmts.append([file_type.upper(), pw.sync_enabled,pw]) for file_type in plugin.file_types] except ImportError: - info("ImportError with %s" % name) + info('ImportError with %s' % name) continue finally: sys.path.remove(plugin.resources_path) else: - info("No dynamic tab resources found for %s" % name) + info('No dynamic tab resources found for %s' % name) self.widgets = sorted(self.widgets, key=lambda x: x.TITLE) @@ -181,7 +181,7 @@ class Catalog(QDialog, Ui_Dialog): ''' cf = str(self.format.currentText()).lower() if cf in ('azw3', 'epub', 'mobi') and hasattr(self.options_widget, 'settings_changed'): - self.options_widget.settings_changed("title/format") + self.options_widget.settings_changed('title/format') @property def fmt_options(self): diff --git a/src/calibre/gui2/dialogs/choose_format.py b/src/calibre/gui2/dialogs/choose_format.py index fcf012141d..5b99105497 100644 --- a/src/calibre/gui2/dialogs/choose_format.py +++ b/src/calibre/gui2/dialogs/choose_format.py @@ -14,7 +14,7 @@ class ChooseFormatDialog(QDialog): def __init__(self, window, msg, formats, show_open_with=False): QDialog.__init__(self, window) self.resize(507, 377) - self.setWindowIcon(QIcon.ic("mimetypes/unknown.png")) + self.setWindowIcon(QIcon.ic('mimetypes/unknown.png')) self.setWindowTitle(_('Choose format')) self.l = l = QVBoxLayout(self) self.msg = QLabel(msg) diff --git a/src/calibre/gui2/dialogs/comments_dialog.py b/src/calibre/gui2/dialogs/comments_dialog.py index 4747a918b9..6aa23c6eb6 100644 --- a/src/calibre/gui2/dialogs/comments_dialog.py +++ b/src/calibre/gui2/dialogs/comments_dialog.py @@ -17,8 +17,8 @@ class CommentsDialog(QDialog): def __init__(self, parent, text, column_name=None): QDialog.__init__(self, parent) - self.setObjectName("CommentsDialog") - self.setWindowTitle(_("Edit comments")) + self.setObjectName('CommentsDialog') + self.setWindowTitle(_('Edit comments')) self.verticalLayout = l = QVBoxLayout(self) self.textbox = tb = Editor(self) self.buttonBox = bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel, self) diff --git a/src/calibre/gui2/dialogs/confirm_delete.py b/src/calibre/gui2/dialogs/confirm_delete.py index ff99773429..e62eec6d7f 100644 --- a/src/calibre/gui2/dialogs/confirm_delete.py +++ b/src/calibre/gui2/dialogs/confirm_delete.py @@ -20,7 +20,7 @@ class Dialog(QDialog): extra_button_choices=None ): QDialog.__init__(self, parent) - self.setWindowTitle(title or _("Are you sure?")) + self.setWindowTitle(title or _('Are you sure?')) self.setWindowIcon(QIcon.ic(icon)) self.l = l = QVBoxLayout(self) self.h = h = QHBoxLayout() @@ -31,14 +31,14 @@ class Dialog(QDialog): self.msg = m = QLabel(self) m.setOpenExternalLinks(True) - m.setMinimumWidth(350), m.setWordWrap(True), m.setObjectName("msg") + m.setMinimumWidth(350), m.setWordWrap(True), m.setObjectName('msg') m.setMaximumHeight(400) m.setText(msg) h.addWidget(self.icon_widget), h.addSpacing(10), h.addWidget(m) - self.again = a = QCheckBox((confirm_msg or _("&Show this warning again")), self) - a.setChecked(True), a.setObjectName("again") + self.again = a = QCheckBox((confirm_msg or _('&Show this warning again')), self) + a.setChecked(True), a.setObjectName('again') a.stateChanged.connect(self.toggle) l.addWidget(a) @@ -49,7 +49,7 @@ class Dialog(QDialog): buttons = QDialogButtonBox.StandardButton.Ok standard_button = QDialogButtonBox.StandardButton.Ok self.buttonBox = bb = QDialogButtonBox(buttons, self) - bb.setObjectName("buttonBox") + bb.setObjectName('buttonBox') bb.setFocus(Qt.FocusReason.OtherFocusReason) bb.accepted.connect(self.accept), bb.rejected.connect(self.reject) self.extra_button_clicked = False diff --git a/src/calibre/gui2/dialogs/custom_recipes.py b/src/calibre/gui2/dialogs/custom_recipes.py index 764760f781..890fd7e1fb 100644 --- a/src/calibre/gui2/dialogs/custom_recipes.py +++ b/src/calibre/gui2/dialogs/custom_recipes.py @@ -342,17 +342,17 @@ class BasicRecipe(QWidget): # {{{ self.oldest_article = o = QSpinBox(self) o.setSuffix(' ' + _('day(s)')) - o.setToolTip(_("The oldest article to download")) + o.setToolTip(_('The oldest article to download')) o.setMinimum(1), o.setMaximum(36500) l.addRow(_('&Oldest article:'), o) self.max_articles = m = QSpinBox(self) m.setMinimum(5), m.setMaximum(100) - m.setToolTip(_("Maximum number of articles to download per feed.")) - l.addRow(_("&Max. number of articles per feed:"), m) + m.setToolTip(_('Maximum number of articles to download per feed.')) + l.addRow(_('&Max. number of articles per feed:'), m) self.fg = fg = QGroupBox(self) - fg.setTitle(_("Feeds in recipe")) + fg.setTitle(_('Feeds in recipe')) self.feeds = f = QListWidget(self) fg.h = QHBoxLayout(fg) fg.h.addWidget(f) @@ -519,7 +519,7 @@ class ChooseBuiltinRecipe(Dialog): # {{{ def __init__(self, recipe_model, parent=None): self.recipe_model = recipe_model - Dialog.__init__(self, _("Choose builtin recipe"), 'choose-builtin-recipe', parent=parent) + Dialog.__init__(self, _('Choose builtin recipe'), 'choose-builtin-recipe', parent=parent) def setup_ui(self): self.l = l = QVBoxLayout(self) @@ -537,7 +537,7 @@ class ChooseBuiltinRecipe(Dialog): # {{{ self.recipe_model.searched.connect(self.search.search_done, type=Qt.ConnectionType.QueuedConnection) self.recipe_model.searched.connect(self.search_done) self.go_button = b = QToolButton(self) - b.setText(_("Go")) + b.setText(_('Go')) b.clicked.connect(self.search.do_search) h = QHBoxLayout() h.addWidget(s), h.addWidget(b) @@ -572,7 +572,7 @@ class CustomRecipes(Dialog): def __init__(self, recipe_model, parent=None): self.recipe_model = recipe_model - Dialog.__init__(self, _("Add custom news source"), 'add-custom-news-source', parent=parent) + Dialog.__init__(self, _('Add custom news source'), 'add-custom-news-source', parent=parent) def setup_ui(self): self.l = l = QVBoxLayout(self) @@ -598,9 +598,9 @@ class CustomRecipes(Dialog): la('document_open.png', _('Load recipe from &file'), _('Load a recipe from a file'), self.load_recipe) la('mimetypes/dir.png', _('&Show recipe files'), _('Show the folder containing all recipe files'), self.show_recipe_files) la('mimetypes/opml.png', _('Import &OPML'), _( - "Import a collection of RSS feeds in OPML format\n" - "Many RSS readers can export their subscribed RSS feeds\n" - "in OPML format"), self.import_opml) + 'Import a collection of RSS feeds in OPML format\n' + 'Many RSS readers can export their subscribed RSS feeds\n' + 'in OPML format'), self.import_opml) s.currentChanged.connect(self.update_button_box) self.update_button_box() diff --git a/src/calibre/gui2/dialogs/data_files_manager.py b/src/calibre/gui2/dialogs/data_files_manager.py index 716d36a727..a9f52a2554 100644 --- a/src/calibre/gui2/dialogs/data_files_manager.py +++ b/src/calibre/gui2/dialogs/data_files_manager.py @@ -415,7 +415,7 @@ class DataFilesManager(Dialog): if q: return error_dialog( self, _('Cannot add'), _( - 'Cannot add these data files to the book because they are already in the book\'s data files folder' + "Cannot add these data files to the book because they are already in the book's data files folder" ), show=True, det_msg='\n'.join(q)) m = {f'{DATA_DIR_NAME}/{os.path.basename(x)}': x for x in files} diff --git a/src/calibre/gui2/dialogs/edit_authors_dialog.py b/src/calibre/gui2/dialogs/edit_authors_dialog.py index 7ae6a5a7d2..f2ba6117d8 100644 --- a/src/calibre/gui2/dialogs/edit_authors_dialog.py +++ b/src/calibre/gui2/dialogs/edit_authors_dialog.py @@ -134,7 +134,7 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): except Exception: pass - self.notes_utilities = NotesUtilities(self.table, "authors", + self.notes_utilities = NotesUtilities(self.table, 'authors', lambda item: int(self.table.item(item.row(), AUTHOR_COLUMN).data(Qt.ItemDataRole.UserRole))) self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText(_('&OK')) @@ -462,7 +462,7 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): ca = m.addAction(_('Copy to author sort')) ca.triggered.connect(self.copy_au_to_aus) m.addSeparator() - ca = m.addAction(QIcon.cached_icon('lt.png'), _("Show books by author in book list")) + ca = m.addAction(QIcon.cached_icon('lt.png'), _('Show books by author in book list')) ca.triggered.connect(self.search_in_book_list) else: ca = m.addAction(_('Copy to author')) diff --git a/src/calibre/gui2/dialogs/match_books.py b/src/calibre/gui2/dialogs/match_books.py index beea89a6b6..4eef521bf6 100644 --- a/src/calibre/gui2/dialogs/match_books.py +++ b/src/calibre/gui2/dialogs/match_books.py @@ -83,7 +83,7 @@ class MatchBooks(QDialog, Ui_MatchBooks): self.books_table.setHorizontalHeaderItem(0, t) t = QTableWidgetItem(_('Authors')) self.books_table.setHorizontalHeaderItem(1, t) - t = QTableWidgetItem(ngettext("Series", 'Series', 1)) + t = QTableWidgetItem(ngettext('Series', 'Series', 1)) self.books_table.setHorizontalHeaderItem(2, t) self.books_table_header_height = self.books_table.height() self.books_table.cellDoubleClicked.connect(self.book_doubleclicked) diff --git a/src/calibre/gui2/dialogs/message_box.py b/src/calibre/gui2/dialogs/message_box.py index 8ad3cfc02e..04d28c7938 100644 --- a/src/calibre/gui2/dialogs/message_box.py +++ b/src/calibre/gui2/dialogs/message_box.py @@ -68,29 +68,29 @@ class MessageBox(QDialog): # {{{ resize_needed = pyqtSignal() def setup_ui(self): - self.setObjectName("Dialog") + self.setObjectName('Dialog') self.resize(497, 235) self.gridLayout = l = QGridLayout(self) - l.setObjectName("gridLayout") + l.setObjectName('gridLayout') self.icon_widget = Icon(self) l.addWidget(self.icon_widget) self.msg = la = QLabel(self) la.setWordWrap(True), la.setMinimumWidth(400) la.setOpenExternalLinks(True) - la.setObjectName("msg") + la.setObjectName('msg') l.addWidget(la, 0, 1, 1, 1) self.det_msg = dm = QTextBrowser(self) dm.setReadOnly(True) - dm.setObjectName("det_msg") + dm.setObjectName('det_msg') l.addWidget(dm, 1, 0, 1, 2) self.bb = bb = QDialogButtonBox(self) bb.setStandardButtons(QDialogButtonBox.StandardButton.Ok) - bb.setObjectName("bb") + bb.setObjectName('bb') bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) l.addWidget(bb, 3, 0, 1, 2) self.toggle_checkbox = tc = QCheckBox(self) - tc.setObjectName("toggle_checkbox") + tc.setObjectName('toggle_checkbox') l.addWidget(tc, 2, 0, 1, 2) def __init__( diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index 86acfda56e..c44076faf2 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -114,8 +114,8 @@ class MyBlockingBusy(QDialog): # {{{ if args.series_map_rules: self.selected_options += 1 if DEBUG: - print("Number of steps for bulk metadata: %d" % self.selected_options) - print("Optionslist: ") + print('Number of steps for bulk metadata: %d' % self.selected_options) + print('Optionslist: ') print(options) self.msg = QLabel(_('Processing %d books, please wait...') % len(ids)) @@ -123,13 +123,13 @@ class MyBlockingBusy(QDialog): # {{{ self.font.setPointSize(self.font.pointSize() + 8) self.msg.setFont(self.font) self.current_step_pb = QProgressBar(self) - self.current_step_pb.setFormat(_("Current step progress: %p %")) + self.current_step_pb.setFormat(_('Current step progress: %p %')) if self.selected_options > 1: # More than one Option needs to be done! Add Overall ProgressBar self.overall_pb = QProgressBar(self) self.overall_pb.setRange(0, self.selected_options) self.overall_pb.setValue(0) - self.overall_pb.setFormat(_("Step %v/%m")) + self.overall_pb.setFormat(_('Step %v/%m')) self._layout.addWidget(self.overall_pb) self._layout.addSpacing(15) self.current_option = 0 @@ -159,9 +159,9 @@ class MyBlockingBusy(QDialog): # {{{ pass def on_progress_update(self, processed_steps): - """ + ''' This signal should be emitted if a step can be traced with numbers. - """ + ''' self.current_step_value += processed_steps self.current_step_pb.setValue(self.current_step_value) @@ -171,10 +171,10 @@ class MyBlockingBusy(QDialog): # {{{ self.overall_pb.setValue(self.current_option) def on_progress_next_step_range(self, steps_of_progress): - """ + ''' If steps_of_progress equals 0 results this in a indetermined ProgressBar Otherwise the range is from 0..steps_of_progress - """ + ''' self.current_step_value = 0 self.current_step_pb.setRange(0, steps_of_progress) @@ -857,8 +857,8 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): 'matching and replacement is to be assigned. You can replace ' 'the text in the field, or prepend or append the matched text. ' 'See <a href="https://docs.python.org/library/re.html">' - 'this reference</a> for more information on Python\'s regular ' - 'expressions, and in particular the \'sub\' function.' + "this reference</a> for more information on Python's regular " + "expressions, and in particular the 'sub' function." ) self.search_mode.addItems(self.s_r_match_modes) @@ -900,9 +900,9 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): self.save_button.clicked.connect(self.s_r_save_query) self.remove_button.clicked.connect(self.s_r_remove_query) - self.queries = JSONConfig("search_replace_queries") + self.queries = JSONConfig('search_replace_queries') self.saved_search_name = '' - self.query_field.addItem("") + self.query_field.addItem('') self.query_field_values = sorted(self.queries, key=sort_key) self.query_field.addItems(self.query_field_values) self.query_field.currentIndexChanged.connect(self.s_r_query_change) @@ -1450,9 +1450,9 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): if self.query_field.currentIndex() == 0: return - if not question_dialog(self, _("Delete saved search/replace"), - _("The selected saved search/replace will be deleted. " - "Are you sure?")): + if not question_dialog(self, _('Delete saved search/replace'), + _('The selected saved search/replace will be deleted. ' + 'Are you sure?')): return item_id = self.query_field.currentIndex() @@ -1481,14 +1481,14 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): if not ok: return if not name: - error_dialog(self, _("Save search/replace"), - _("You must provide a name."), show=True) + error_dialog(self, _('Save search/replace'), + _('You must provide a name.'), show=True) new = True name = str(name) if name in list(self.queries.keys()): - if not question_dialog(self, _("Save search/replace"), - _("That saved search/replace already exists and will be overwritten. " - "Are you sure?")): + if not question_dialog(self, _('Save search/replace'), + _('That saved search/replace already exists and will be overwritten. ' + 'Are you sure?')): return new = False @@ -1580,10 +1580,10 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): # as it was self.search_field.setCurrentIndex(0) self.s_r_src_ident.setCurrentIndex(0) - self.s_r_template.setText("") - self.search_for.setText("") + self.s_r_template.setText('') + self.search_for.setText('') self.case_sensitive.setChecked(False) - self.replace_with.setText("") + self.replace_with.setText('') self.replace_func.setCurrentIndex(0) self.destination_field.setCurrentIndex(0) self.s_r_dst_ident.setText('') @@ -1591,4 +1591,4 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): self.comma_separated.setChecked(True) self.results_count.setValue(999) self.starting_from.setValue(1) - self.multiple_separator.setText(" ::: ") + self.multiple_separator.setText(' ::: ') diff --git a/src/calibre/gui2/dialogs/multisort.py b/src/calibre/gui2/dialogs/multisort.py index 07cb1dbe7e..7ed4a24001 100644 --- a/src/calibre/gui2/dialogs/multisort.py +++ b/src/calibre/gui2/dialogs/multisort.py @@ -108,7 +108,7 @@ class ChooseMultiSort(Dialog): if i != 0: t += ' :: ' q = bytes.hex(k.encode('utf-8')) - dname = prepare_string_for_xml(name).replace(" ", " ") + dname = prepare_string_for_xml(name).replace(' ', ' ') t += f' <a href="{q}" style="text-decoration: none">{dname} {symbol}</a>' if t: t = _('Effective sort') + ': ' + t diff --git a/src/calibre/gui2/dialogs/plugin_updater.py b/src/calibre/gui2/dialogs/plugin_updater.py index 51787ea17f..1487c2aaab 100644 --- a/src/calibre/gui2/dialogs/plugin_updater.py +++ b/src/calibre/gui2/dialogs/plugin_updater.py @@ -248,7 +248,7 @@ class DisplayPluginSortFilterModel(QSortFilterProxyModel): self.setSortRole(Qt.ItemDataRole.UserRole) self.setSortCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive) self.filter_criteria = FILTER_ALL - self.filter_text = "" + self.filter_text = '' def filterAcceptsRow(self, sourceRow, sourceParent): index = self.sourceModel().index(sourceRow, 0, sourceParent) @@ -509,7 +509,7 @@ class PluginUpdaterDialog(SizePersistedDialog): header_layout.addWidget(la) self.filter_by_name_lineedit = QLineEdit(self) la.setBuddy(self.filter_by_name_lineedit) - self.filter_by_name_lineedit.setText("") + self.filter_by_name_lineedit.setText('') self.filter_by_name_lineedit.textChanged.connect(self._filter_name_lineedit_changed) header_layout.addWidget(self.filter_by_name_lineedit) @@ -652,7 +652,7 @@ class PluginUpdaterDialog(SizePersistedDialog): self.plugin_view.setFocus() def _filter_combo_changed(self, idx): - self.filter_by_name_lineedit.setText("") # clear the name filter text when a different group was selected + self.filter_by_name_lineedit.setText('') # clear the name filter text when a different group was selected self.proxy_model.set_filter_criteria(idx) if idx == FILTER_NOT_INSTALLED: self.plugin_view.sortByColumn(5, Qt.SortOrder.DescendingOrder) diff --git a/src/calibre/gui2/dialogs/quickview.py b/src/calibre/gui2/dialogs/quickview.py index 249054d51a..1d1e1b286e 100644 --- a/src/calibre/gui2/dialogs/quickview.py +++ b/src/calibre/gui2/dialogs/quickview.py @@ -817,7 +817,7 @@ class Quickview(QDialog, Ui_Quickview): self.edit_metadata(book_id) else: if key not in self.view.visible_columns: - error_dialog(self, _("Quickview: Column cannot be selected"), + error_dialog(self, _('Quickview: Column cannot be selected'), _("The column you double-clicked, '{}', is not shown in the " "library view. The book/column cannot be selected by Quickview.").format(key), show=True, diff --git a/src/calibre/gui2/dialogs/restore_library.py b/src/calibre/gui2/dialogs/restore_library.py index 4997e9e1d8..99c1b6107a 100644 --- a/src/calibre/gui2/dialogs/restore_library.py +++ b/src/calibre/gui2/dialogs/restore_library.py @@ -97,7 +97,7 @@ def restore_database(db, parent=None): _('Your list of books, with all their metadata is ' 'stored in a single file, called a database. ' 'In addition, metadata for each individual ' - 'book is stored in that books\' folder, as ' + "book is stored in that books' folder, as " 'a backup.' '<p>This operation will rebuild ' 'the database from the individual book ' diff --git a/src/calibre/gui2/dialogs/scheduler.py b/src/calibre/gui2/dialogs/scheduler.py index 49eac21e21..c8c70457ac 100644 --- a/src/calibre/gui2/dialogs/scheduler.py +++ b/src/calibre/gui2/dialogs/scheduler.py @@ -268,7 +268,7 @@ class SchedulerDialog(QDialog): self.search.initialize('scheduler_search_history') self.search.setMinimumContentsLength(15) self.go_button = b = QToolButton(self) - b.setText(_("Go")) + b.setText(_('Go')) b.clicked.connect(self.search.do_search) h.addWidget(s), h.addWidget(b) self.recipes = RecipesView(self) @@ -278,7 +278,7 @@ class SchedulerDialog(QDialog): self.recipes.setModel(self.recipe_model) self.recipes.setFocus(Qt.FocusReason.OtherFocusReason) self.recipes.item_activated.connect(self.download_clicked) - self.setWindowTitle(_("Schedule news download [{} sources]").format(self.recipe_model.showing_count)) + self.setWindowTitle(_('Schedule news download [{} sources]').format(self.recipe_model.showing_count)) self.search.search.connect(self.recipe_model.search) self.recipe_model.searched.connect(self.search.search_done, type=Qt.ConnectionType.QueuedConnection) self.recipe_model.searched.connect(self.search_done) @@ -296,7 +296,7 @@ class SchedulerDialog(QDialog): # First Tab (scheduling) self.tab = QWidget() - self.detail_box.addTab(self.tab, _("&Schedule")) + self.detail_box.addTab(self.tab, _('&Schedule')) self.tab.v = vt = QVBoxLayout(self.tab) self.blurb = la = QLabel('blurb') la.setWordWrap(True), la.setOpenExternalLinks(True) @@ -306,14 +306,14 @@ class SchedulerDialog(QDialog): f.setFrameShape(QFrame.Shape.StyledPanel) f.setFrameShadow(QFrame.Shadow.Raised) f.v = vf = QVBoxLayout(f) - self.schedule = s = QCheckBox(_("&Schedule for download:"), f) + self.schedule = s = QCheckBox(_('&Schedule for download:'), f) self.schedule.stateChanged[int].connect(self.toggle_schedule_info) vf.addWidget(s) f.h = h = QHBoxLayout() vf.addLayout(h) - self.days_of_week = QRadioButton(_("&Days of week"), f) - self.days_of_month = QRadioButton(_("Da&ys of month"), f) - self.every_x_days = QRadioButton(_("Every &x days"), f) + self.days_of_week = QRadioButton(_('&Days of week'), f) + self.days_of_month = QRadioButton(_('Da&ys of month'), f) + self.every_x_days = QRadioButton(_('Every &x days'), f) self.days_of_week.setChecked(True) h.addWidget(self.days_of_week), h.addWidget(self.days_of_month), h.addWidget(self.every_x_days) self.schedule_stack = ss = QStackedWidget(f) @@ -325,22 +325,22 @@ class SchedulerDialog(QDialog): self.last_downloaded = la = QLabel(f) la.setWordWrap(True) vf.addWidget(la) - self.rla = la = QLabel(_("For the scheduling to work, you must leave calibre running.")) + self.rla = la = QLabel(_('For the scheduling to work, you must leave calibre running.')) la.setWordWrap(True) vf.addWidget(la) self.account = acc = QGroupBox(self.tab) - acc.setTitle(_("&Account")) + acc.setTitle(_('&Account')) vt.addWidget(acc) acc.g = g = QGridLayout(acc) - acc.unla = la = QLabel(_("&Username:")) + acc.unla = la = QLabel(_('&Username:')) self.username = un = QLineEdit(self) la.setBuddy(un) g.addWidget(la), g.addWidget(un, 0, 1) - acc.pwla = la = QLabel(_("&Password:")) + acc.pwla = la = QLabel(_('&Password:')) self.password = pw = QLineEdit(self) pw.setEchoMode(QLineEdit.EchoMode.Password), la.setBuddy(pw) g.addWidget(la), g.addWidget(pw, 1, 1) - self.show_password = spw = QCheckBox(_("&Show password"), self.account) + self.show_password = spw = QCheckBox(_('&Show password'), self.account) spw.stateChanged[int].connect(self.set_pw_echo_mode) g.addWidget(spw, 2, 0, 1, 2) for b, c in iteritems(self.SCHEDULE_TYPES): @@ -350,41 +350,41 @@ class SchedulerDialog(QDialog): # Second tab (advanced settings) self.tab2 = t2 = QWidget() - self.detail_box.addTab(self.tab2, _("&Advanced")) + self.detail_box.addTab(self.tab2, _('&Advanced')) self.tab2.g = g = QFormLayout(t2) g.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow) - self.add_title_tag = tt = QCheckBox(_("Add &title as tag"), t2) + self.add_title_tag = tt = QCheckBox(_('Add &title as tag'), t2) g.addRow(tt) self.custom_tags = ct = QLineEdit(self) - g.addRow(_("&Extra tags:"), ct) + g.addRow(_('&Extra tags:'), ct) self.keep_issues = ki = QSpinBox(t2) tt.toggled['bool'].connect(self.keep_issues.setEnabled) ki.setMaximum(100000) ki.setToolTip(_( - "<p>When set, this option will cause calibre to keep, at most, the specified number of issues" - " of this periodical. Every time a new issue is downloaded, the oldest one is deleted, if the" - " total is larger than this number.\n<p>Note that this feature only works if you have the" - " option to add the title as tag checked, above.\n<p>Also, the setting for deleting periodicals" - " older than a number of days, below, takes priority over this setting.")) - ki.setSpecialValueText(_("all issues")), ki.setSuffix(_(" issues")) - g.addRow(_("&Keep at most:"), ki) + '<p>When set, this option will cause calibre to keep, at most, the specified number of issues' + ' of this periodical. Every time a new issue is downloaded, the oldest one is deleted, if the' + ' total is larger than this number.\n<p>Note that this feature only works if you have the' + ' option to add the title as tag checked, above.\n<p>Also, the setting for deleting periodicals' + ' older than a number of days, below, takes priority over this setting.')) + ki.setSpecialValueText(_('all issues')), ki.setSuffix(_(' issues')) + g.addRow(_('&Keep at most:'), ki) self.recipe_specific_widgets = {} # Bottom area self.hb = h = QHBoxLayout() self.l.addLayout(h, 2, 1, 1, 1) - self.labt = la = QLabel(_("Delete downloaded &news older than:")) + self.labt = la = QLabel(_('Delete downloaded &news older than:')) self.old_news = on = QSpinBox(self) on.setToolTip(_( - "<p>Delete downloaded news older than the specified number of days. Set to zero to disable.\n" - "<p>You can also control the maximum number of issues of a specific periodical that are kept" - " by clicking the Advanced tab for that periodical above.")) - on.setSpecialValueText(_("never delete")), on.setSuffix(_(" days")) + '<p>Delete downloaded news older than the specified number of days. Set to zero to disable.\n' + '<p>You can also control the maximum number of issues of a specific periodical that are kept' + ' by clicking the Advanced tab for that periodical above.')) + on.setSpecialValueText(_('never delete')), on.setSuffix(_(' days')) on.setMaximum(1000), la.setBuddy(on) on.setValue(gconf['oldest_news']) h.addWidget(la), h.addWidget(on) - self.download_all_button = b = QPushButton(QIcon.ic('news.png'), _("Download &all scheduled"), self) - b.setToolTip(_("Download all scheduled news sources at once")) + self.download_all_button = b = QPushButton(QIcon.ic('news.png'), _('Download &all scheduled'), self) + b.setToolTip(_('Download all scheduled news sources at once')) b.clicked.connect(self.download_all_clicked) self.l.addWidget(b, 3, 0, 1, 1) self.bb = bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel, self) diff --git a/src/calibre/gui2/dialogs/search.py b/src/calibre/gui2/dialogs/search.py index 106806538d..46c69e3412 100644 --- a/src/calibre/gui2/dialogs/search.py +++ b/src/calibre/gui2/dialogs/search.py @@ -65,9 +65,9 @@ def create_msg_label(self): f.setFrameShadow(QFrame.Shadow.Raised) f.l = l = QVBoxLayout(f) f.um_label = la = QLabel(_( - "<p>You can also perform other kinds of advanced searches, for example checking" + '<p>You can also perform other kinds of advanced searches, for example checking' ' for books that have no covers, combining multiple search expression using Boolean' - ' operators and so on. See <a href=\"%s\">The search interface</a> for more information.' + ' operators and so on. See <a href="%s">The search interface</a> for more information.' ) % localize_user_manual_link('https://manual.calibre-ebook.com/gui.html#the-search-interface')) la.setMinimumSize(QSize(150, 0)) la.setWordWrap(True) @@ -77,13 +77,13 @@ def create_msg_label(self): def create_match_kind(self): - self.cmk_label = la = QLabel(_("What &kind of match to use:")) + self.cmk_label = la = QLabel(_('What &kind of match to use:')) self.matchkind = m = QComboBox(self) la.setBuddy(m) m.addItems([ - _("Contains: the word or phrase matches anywhere in the metadata field"), - _("Equals: the word or phrase must match the entire metadata field"), - _("Regular expression: the expression must match anywhere in the metadata field"), + _('Contains: the word or phrase matches anywhere in the metadata field'), + _('Equals: the word or phrase must match the entire metadata field'), + _('Regular expression: the expression must match anywhere in the metadata field'), _("Character variant: 'contains' with accents ignored and punctuation significant") ]) l = QHBoxLayout() @@ -102,9 +102,9 @@ def create_button_box(self): def create_adv_tab(self): self.adv_tab = w = QWidget(self.tab_widget) - self.tab_widget.addTab(w, _("A&dvanced search")) + self.tab_widget.addTab(w, _('A&dvanced search')) - w.g1 = QGroupBox(_("Find entries that have..."), w) + w.g1 = QGroupBox(_('Find entries that have...'), w) w.g2 = QGroupBox(_("But don't show entries that have..."), w) w.l = l = QVBoxLayout(w) l.addWidget(w.g1), l.addWidget(w.g2), l.addStretch(10) @@ -112,9 +112,9 @@ def create_adv_tab(self): w.g1.l = l = QFormLayout(w.g1) l.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow) for key, text in ( - ('all', _("A&ll these words:")), - ('phrase', _("&This exact phrase:")), - ('any', _("O&ne or more of these words:")), + ('all', _('A&ll these words:')), + ('phrase', _('&This exact phrase:')), + ('any', _('O&ne or more of these words:')), ): le = QLineEdit(w) le.setClearButtonEnabled(True) @@ -125,12 +125,12 @@ def create_adv_tab(self): l.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow) self.none = le = QLineEdit(w) le.setClearButtonEnabled(True) - l.addRow(_("Any of these &unwanted words:"), le) + l.addRow(_('Any of these &unwanted words:'), le) def create_simple_tab(self, db): self.simple_tab = w = QWidget(self.tab_widget) - self.tab_widget.addTab(w, _("Titl&e/author/series...")) + self.tab_widget.addTab(w, _('Titl&e/author/series...')) w.l = l = QFormLayout(w) l.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow) @@ -198,7 +198,7 @@ def toggle_date_conditions_visibility(self): def create_date_tab(self, db): self.date_tab = w = QWidget(self.tab_widget) w.date_condition_layouts = dcl = [] - self.tab_widget.addTab(w, _("&Date search")) + self.tab_widget.addTab(w, _('&Date search')) w.l = l = QVBoxLayout(w) def a(w): @@ -213,7 +213,7 @@ def create_date_tab(self, db): w.h1 = h = QHBoxLayout() l.addLayout(h) - self.date_field = df = add(_("&Search the"), QComboBox(w)) + self.date_field = df = add(_('&Search the'), QComboBox(w)) vals = [((v['search_terms'] or [k])[0], v['name'] or k) for k, v in db.field_metadata.iter_items() if v.get('datatype', None) == 'datetime' or @@ -222,7 +222,7 @@ def create_date_tab(self, db): for k, v in sorted(vals, key=lambda k_v: sort_key(k_v[1])): df.addItem(v, k) h.addWidget(df) - self.dateop_date = dd = add(_("date column for books whose &date is "), QComboBox(w)) + self.dateop_date = dd = add(_('date column for books whose &date is '), QComboBox(w)) init_dateop(dd) connect_lambda(dd.currentIndexChanged, self, toggle_date_conditions_visibility) w.la3 = la = QLabel('...') @@ -276,7 +276,7 @@ def create_date_tab(self, db): def create_template_tab(self): self.simple_tab = w = QWidget(self.tab_widget) - self.tab_widget.addTab(w, _("&Template search")) + self.tab_widget.addTab(w, _('&Template search')) w.l = l = QFormLayout(w) l.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow) @@ -323,7 +323,7 @@ def create_template_tab(self): lo = QHBoxLayout() lo.addWidget(le) self.edit_template_button = tb = QToolButton() - tb.setIcon(QIcon.ic("edit_input.png")) + tb.setIcon(QIcon.ic('edit_input.png')) tb.setToolTip(_('Open template editor')) lo.addWidget(tb) self.template_layout_label = tll = QLabel(_('&Template:')) @@ -337,7 +337,7 @@ def create_template_tab(self): def setup_ui(self, db): - self.setWindowTitle(_("Advanced search")) + self.setWindowTitle(_('Advanced search')) self.setWindowIcon(QIcon.ic('search.png')) self.l = l = QVBoxLayout(self) self.h = h = QHBoxLayout() diff --git a/src/calibre/gui2/dialogs/tag_list_editor.py b/src/calibre/gui2/dialogs/tag_list_editor.py index de35381fda..deb766b886 100644 --- a/src/calibre/gui2/dialogs/tag_list_editor.py +++ b/src/calibre/gui2/dialogs/tag_list_editor.py @@ -513,7 +513,7 @@ class TagListEditor(QDialog, Ui_TagListEditor): ca.setEnabled(not item.is_deleted) if self.category is not None: - ca = m.addAction(_("Search the library for {0}").format(item_name)) + ca = m.addAction(_('Search the library for {0}').format(item_name)) ca.setIcon(QIcon.cached_icon('lt.png')) ca.triggered.connect(partial(self.search_for_books, item)) ca.setEnabled(not item.is_deleted) @@ -916,8 +916,8 @@ class TagListEditor(QDialog, Ui_TagListEditor): new_text = str(edited_item.text()) if self.is_enumerated and new_text not in self.enum_permitted_values: error_dialog(self, _('Item is not a permitted value'), '<p>' + _( - "This column has a fixed set of permitted values. The entered " - "text must be one of ({0}).").format(', '.join(self.enum_permitted_values)) + + 'This column has a fixed set of permitted values. The entered ' + 'text must be one of ({0}).').format(', '.join(self.enum_permitted_values)) + '</p>', show=True) with block_signals(self.table): edited_item.setText(self.text_before_editing) diff --git a/src/calibre/gui2/dialogs/template_dialog.py b/src/calibre/gui2/dialogs/template_dialog.py index f65b32283f..9eb389bdef 100644 --- a/src/calibre/gui2/dialogs/template_dialog.py +++ b/src/calibre/gui2/dialogs/template_dialog.py @@ -215,24 +215,24 @@ class TemplateHighlighter(QSyntaxHighlighter): 'separator', 'break', 'continue', 'return', 'in', 'inlist', 'inlist_field', 'def', 'fed', 'limit'] - KEYWORDS_PYTHON = ["and", "as", "assert", "break", "class", "continue", "def", - "del", "elif", "else", "except", "exec", "finally", "for", "from", - "global", "if", "import", "in", "is", "lambda", "not", "or", - "pass", "print", "raise", "return", "try", "while", "with", - "yield"] + KEYWORDS_PYTHON = ['and', 'as', 'assert', 'break', 'class', 'continue', 'def', + 'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from', + 'global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', + 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', + 'yield'] - BUILTINS_PYTHON = ["abs", "all", "any", "basestring", "bool", "callable", "chr", - "classmethod", "cmp", "compile", "complex", "delattr", "dict", - "dir", "divmod", "enumerate", "eval", "execfile", "exit", "file", - "filter", "float", "frozenset", "getattr", "globals", "hasattr", - "hex", "id", "int", "isinstance", "issubclass", "iter", "len", - "list", "locals", "long", "map", "max", "min", "object", "oct", - "open", "ord", "pow", "property", "range", "reduce", "repr", - "reversed", "round", "set", "setattr", "slice", "sorted", - "staticmethod", "str", "sum", "super", "tuple", "type", "unichr", - "unicode", "vars", "xrange", "zip"] + BUILTINS_PYTHON = ['abs', 'all', 'any', 'basestring', 'bool', 'callable', 'chr', + 'classmethod', 'cmp', 'compile', 'complex', 'delattr', 'dict', + 'dir', 'divmod', 'enumerate', 'eval', 'execfile', 'exit', 'file', + 'filter', 'float', 'frozenset', 'getattr', 'globals', 'hasattr', + 'hex', 'id', 'int', 'isinstance', 'issubclass', 'iter', 'len', + 'list', 'locals', 'long', 'map', 'max', 'min', 'object', 'oct', + 'open', 'ord', 'pow', 'property', 'range', 'reduce', 'repr', + 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', + 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', + 'unicode', 'vars', 'xrange', 'zip'] - CONSTANTS_PYTHON = ["False", "True", "None", "NotImplemented", "Ellipsis"] + CONSTANTS_PYTHON = ['False', 'True', 'None', 'NotImplemented', 'Ellipsis'] def __init__(self, parent=None, builtin_functions=None): super().__init__(parent) @@ -249,38 +249,38 @@ class TemplateHighlighter(QSyntaxHighlighter): r.append((re.compile(a), b)) if not for_python: - a(r"\b[a-zA-Z]\w*\b(?!\(|\s+\()" - r"|\$+#?[a-zA-Z]\w*", - "identifier") - a(r"^program:", "keymode") - a("|".join([r"\b%s\b" % keyword for keyword in self.KEYWORDS_GPM]), "keyword") - a("|".join([r"\b%s\b" % builtin for builtin in + a(r'\b[a-zA-Z]\w*\b(?!\(|\s+\()' + r'|\$+#?[a-zA-Z]\w*', + 'identifier') + a(r'^program:', 'keymode') + a('|'.join([r'\b%s\b' % keyword for keyword in self.KEYWORDS_GPM]), 'keyword') + a('|'.join([r'\b%s\b' % builtin for builtin in (builtin_functions if builtin_functions else formatter_functions().get_builtins())]), - "builtin") - a(r"""(?<!:)'[^']*'|"[^"]*\"""", "string") + 'builtin') + a(r'''(?<!:)'[^']*'|"[^"]*\"''', 'string') else: - a(r"^python:", "keymode") - a("|".join([r"\b%s\b" % keyword for keyword in self.KEYWORDS_PYTHON]), "keyword") - a("|".join([r"\b%s\b" % builtin for builtin in self.BUILTINS_PYTHON]), "builtin") - a("|".join([r"\b%s\b" % constant for constant in self.CONSTANTS_PYTHON]), "constant") - a(r"\bPyQt6\b|\bqt.core\b|\bQt?[A-Z][a-z]\w+\b", "pyqt") - a(r"@\w+(\.\w+)?\b", "decorator") + a(r'^python:', 'keymode') + a('|'.join([r'\b%s\b' % keyword for keyword in self.KEYWORDS_PYTHON]), 'keyword') + a('|'.join([r'\b%s\b' % builtin for builtin in self.BUILTINS_PYTHON]), 'builtin') + a('|'.join([r'\b%s\b' % constant for constant in self.CONSTANTS_PYTHON]), 'constant') + a(r'\bPyQt6\b|\bqt.core\b|\bQt?[A-Z][a-z]\w+\b', 'pyqt') + a(r'@\w+(\.\w+)?\b', 'decorator') stringRe = r'''(["'])(?:(?!\1)[^\\]|\\.)*\1''' - a(stringRe, "string") + a(stringRe, 'string') self.stringRe = re.compile(stringRe) - self.checkTripleInStringRe = re.compile(r"""((?:"|'){3}).*?\1""") + self.checkTripleInStringRe = re.compile(r'''((?:"|'){3}).*?\1''') self.tripleSingleRe = re.compile(r"""'''(?!")""") self.tripleDoubleRe = re.compile(r'''"""(?!')''') a( - r"\b[+-]?[0-9]+[lL]?\b" - r"|\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b" - r"|\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b", - "number") + r'\b[+-]?[0-9]+[lL]?\b' + r'|\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b' + r'|\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', + 'number') - a(r'\(', "lparen") - a(r'\)', "rparen") + a(r'\(', 'lparen') + a(r'\)', 'rparen') self.Rules = tuple(r) def initialize_formats(self): @@ -292,43 +292,43 @@ class TemplateHighlighter(QSyntaxHighlighter): font.setPointSize(size) font_name = font.family() config = self.Config = {} - config["fontfamily"] = font_name + config['fontfamily'] = font_name app_palette = QApplication.instance().palette() is_dark = QApplication.instance().is_dark_theme all_formats = ( # name, color, bold, italic - ("normal", None, False, False), - ("keyword", app_palette.color(QPalette.ColorRole.Link).name(), True, False), - ("builtin", app_palette.color(QPalette.ColorRole.Link).name(), False, False), - ("constant", app_palette.color(QPalette.ColorRole.Link).name(), False, False), - ("identifier", None, False, True), - ("comment", '#00c700' if is_dark else "#007F00", False, True), - ("string", '#b6b600' if is_dark else "#808000", False, False), - ("number", '#d96d00' if is_dark else "#924900", False, False), - ("decorator", "#FF8000", False, True), - ("pyqt", None, False, False), - ("lparen", None, True, True), - ("rparen", None, True, True)) + ('normal', None, False, False), + ('keyword', app_palette.color(QPalette.ColorRole.Link).name(), True, False), + ('builtin', app_palette.color(QPalette.ColorRole.Link).name(), False, False), + ('constant', app_palette.color(QPalette.ColorRole.Link).name(), False, False), + ('identifier', None, False, True), + ('comment', '#00c700' if is_dark else '#007F00', False, True), + ('string', '#b6b600' if is_dark else '#808000', False, False), + ('number', '#d96d00' if is_dark else '#924900', False, False), + ('decorator', '#FF8000', False, True), + ('pyqt', None, False, False), + ('lparen', None, True, True), + ('rparen', None, True, True)) for name, color, bold, italic in all_formats: - config["%sfontcolor" % name] = color - config["%sfontbold" % name] = bold - config["%sfontitalic" % name] = italic + config['%sfontcolor' % name] = color + config['%sfontbold' % name] = bold + config['%sfontitalic' % name] = italic base_format = QTextCharFormat() - base_format.setFontFamilies([config["fontfamily"]]) - config["fontsize"] = size - base_format.setFontPointSize(config["fontsize"]) + base_format.setFontFamilies([config['fontfamily']]) + config['fontsize'] = size + base_format.setFontPointSize(config['fontsize']) self.Formats = {} for name, color, bold, italic in all_formats: format_ = QTextCharFormat(base_format) - color = config["%sfontcolor" % name] + color = config['%sfontcolor' % name] if color: format_.setForeground(QColor(color)) - if config["%sfontbold" % name]: + if config['%sfontbold' % name]: format_.setFontWeight(QFont.Weight.Bold) - format_.setFontItalic(config["%sfontitalic" % name]) + format_.setFontItalic(config['%sfontitalic' % name]) self.Formats[name] = format_ def find_paren(self, bn, pos): @@ -345,12 +345,12 @@ class TemplateHighlighter(QSyntaxHighlighter): bn = self.currentBlock().blockNumber() textLength = len(text) - self.setFormat(0, textLength, self.Formats["normal"]) + self.setFormat(0, textLength, self.Formats['normal']) if not text: pass - elif text[0] == "#": - self.setFormat(0, textLength, self.Formats["comment"]) + elif text[0] == '#': + self.setFormat(0, textLength, self.Formats['comment']) return for regex, format_ in self.Rules: @@ -374,7 +374,7 @@ class TemplateHighlighter(QSyntaxHighlighter): t = re.sub(self.stringRe, self.replace_strings_with_dash, text) sharp_pos = t.find('#') if sharp_pos >= 0: # Do we still have a #? - self.setFormat(sharp_pos, len(text), self.Formats["comment"]) + self.setFormat(sharp_pos, len(text), self.Formats['comment']) self.setCurrentBlockState(NORMAL) @@ -389,10 +389,10 @@ class TemplateHighlighter(QSyntaxHighlighter): if i == -1: i = len(text) self.setCurrentBlockState(state) - self.setFormat(0, i + 3, self.Formats["string"]) + self.setFormat(0, i + 3, self.Formats['string']) elif i > -1: self.setCurrentBlockState(state) - self.setFormat(i, len(text), self.Formats["string"]) + self.setFormat(i, len(text), self.Formats['string']) if self.generate_paren_positions: t = str(text) @@ -777,7 +777,7 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): tv.setCellWidget(r, 0, w) tb = QToolButton() tb.setContentsMargins(0, 0, 0, 0) - tb.setIcon(QIcon.ic("edit_input.png")) + tb.setIcon(QIcon.ic('edit_input.png')) tb.setToolTip(_('Open Edit metadata on this book')) tb.clicked.connect(partial(self.metadata_button_clicked, r)) tb.setEnabled(mi[r].get('id', -1) >= 0) diff --git a/src/calibre/gui2/font_family_chooser.py b/src/calibre/gui2/font_family_chooser.py index 387785a6eb..2a824dd6a1 100644 --- a/src/calibre/gui2/font_family_chooser.py +++ b/src/calibre/gui2/font_family_chooser.py @@ -160,7 +160,7 @@ class FontFamilyDelegate(QStyledItemDelegate): painter.drawText(r, Qt.AlignmentFlag.AlignVCenter|Qt.AlignmentFlag.AlignLeading|Qt.TextFlag.TextSingleLine, text) if (system != QFontDatabase.WritingSystem.Any): - w = painter.fontMetrics().horizontalAdvance(text + " ") + w = painter.fontMetrics().horizontalAdvance(text + ' ') painter.setFont(font2) sample = QFontDatabase.writingSystemSample(system) if (option.direction == Qt.LayoutDirection.RightToLeft): diff --git a/src/calibre/gui2/icon_theme.py b/src/calibre/gui2/icon_theme.py index 9fbde53a9a..f36484f619 100644 --- a/src/calibre/gui2/icon_theme.py +++ b/src/calibre/gui2/icon_theme.py @@ -774,7 +774,7 @@ class ChooseThemeWidget(QWidget): return default_theme() def set_current_theme(self, name): - if not hasattr(self, "themes"): + if not hasattr(self, 'themes'): return False for i, t in enumerate(self.themes): if t.get('name') == name: diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index cb2d1eb233..649e670c0d 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -141,7 +141,7 @@ class VersionLabel(QLabel): # {{{ QLabel.__init__(self, parent) self.mouse_over = False self.setCursor(Qt.CursorShape.PointingHandCursor) - self.setToolTip(_('See what\'s new in this calibre release')) + self.setToolTip(_("See what's new in this calibre release")) def mouseReleaseEvent(self, ev): open_url(localize_website_link('https://calibre-ebook.com/whats-new')) diff --git a/src/calibre/gui2/job_indicator.py b/src/calibre/gui2/job_indicator.py index 504867a409..173c9aac2c 100644 --- a/src/calibre/gui2/job_indicator.py +++ b/src/calibre/gui2/job_indicator.py @@ -17,7 +17,7 @@ class Pointer(QWidget): self.setObjectName('jobs_pointer') self.setVisible(False) self.resize(100, 80) - self.animation = QPropertyAnimation(self, b"geometry", self) + self.animation = QPropertyAnimation(self, b'geometry', self) self.animation.setDuration(750) self.animation.setLoopCount(2) self.animation.setEasingCurve(QEasingCurve.Type.Linear) diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index 33c93b5a2f..9741c7c9f2 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -212,7 +212,7 @@ class SearchBar(QFrame): # {{{ x.setText(_('Virtual library')) x.setAutoRaise(True) x.setIcon(QIcon.ic('vl.png')) - x.setObjectName("virtual_library") + x.setObjectName('virtual_library') x.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon) l.addWidget(x) @@ -241,9 +241,9 @@ class SearchBar(QFrame): # {{{ x = parent.search = SearchBox2(self, as_url=search_as_url) x.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) - x.setObjectName("search") - x.setToolTip(_("<p>Search the list of books by title, author, publisher, " - "tags, comments, etc.<br><br>Words separated by spaces are ANDed")) + x.setObjectName('search') + x.setToolTip(_('<p>Search the list of books by title, author, publisher, ' + 'tags, comments, etc.<br><br>Words separated by spaces are ANDed')) x.setMinimumContentsLength(10) l.addWidget(x) @@ -255,7 +255,7 @@ class SearchBar(QFrame): # {{{ parent.addAction(ac) ac.setToolTip(_('Advanced search')) parent.keyboard.register_shortcut('advanced search toggle', - _('Advanced search'), default_keys=("Shift+Ctrl+F",), + _('Advanced search'), default_keys=('Shift+Ctrl+F',), action=ac) # This error icon will be placed after the clear button icon diff --git a/src/calibre/gui2/library/annotations.py b/src/calibre/gui2/library/annotations.py index 2ef18eb728..c31f86e93e 100644 --- a/src/calibre/gui2/library/annotations.py +++ b/src/calibre/gui2/library/annotations.py @@ -269,7 +269,7 @@ def css_for_highlight_style(style): elif 'background-color' in style: ans = 'background-color: ' + style['background-color'] if 'color' in style: - ans += '; color: ' + style["color"] + ans += '; color: ' + style['color'] elif kind == 'decoration': which = style.get('which') if which is not None: diff --git a/src/calibre/gui2/library/delegates.py b/src/calibre/gui2/library/delegates.py index eb2b4a4c70..ad434bed8e 100644 --- a/src/calibre/gui2/library/delegates.py +++ b/src/calibre/gui2/library/delegates.py @@ -923,7 +923,7 @@ class CcTemplateDelegate(StyledItemDelegate): # {{{ else: text = m.custom_columns[m.column_map[index.column()]]['display']['composite_template'] editor = TemplateDialog(parent, text, mi) - editor.setWindowTitle(_("Edit template")) + editor.setWindowTitle(_('Edit template')) editor.textbox.setTabChangesFocus(False) editor.textbox.setTabStopDistance(20) d = editor.exec() diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 127c6d2a85..3a65449878 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -204,16 +204,16 @@ class BooksModel(QAbstractTableModel): # {{{ self.bi_font.setItalic(True) self.styled_columns = {} self.orig_headers = { - 'title' : _("Title"), - 'ondevice' : _("On Device"), - 'authors' : _("Author(s)"), - 'size' : _("Size (MB)"), - 'timestamp' : _("Date"), + 'title' : _('Title'), + 'ondevice' : _('On Device'), + 'authors' : _('Author(s)'), + 'size' : _('Size (MB)'), + 'timestamp' : _('Date'), 'pubdate' : _('Published'), 'rating' : _('Rating'), - 'publisher' : _("Publisher"), - 'tags' : _("Tags"), - 'series' : ngettext("Series", 'Series', 1), + 'publisher' : _('Publisher'), + 'tags' : _('Tags'), + 'series' : ngettext('Series', 'Series', 1), 'last_modified' : _('Modified'), 'languages' : _('Languages'), 'formats' : _('Formats'), @@ -1840,7 +1840,7 @@ class DeviceBooksModel(BooksModel): # {{{ cname == 'collections' and ( callable(getattr(self.db, 'supports_collections', None)) and self.db.supports_collections()) ): - return (_("Double click to <b>edit</b> me<br><br>")) + return (_('Double click to <b>edit</b> me<br><br>')) elif role == Qt.ItemDataRole.DecorationRole and cname == 'inlibrary': if hasattr(self.db[self.map[row]], 'in_library_waiting'): return (self.sync_icon) diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index dac15f7768..c66c35b4ae 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -751,7 +751,7 @@ class BooksView(QTableView): # {{{ ac = getattr(m, 'column_mouse_move_action', None) if ac is None: - ac = m.column_mouse_move_action = m.addAction(_("Allow moving columns with the mouse"), + ac = m.column_mouse_move_action = m.addAction(_('Allow moving columns with the mouse'), partial(self.column_header_context_handler, action='lock', column=col, view=view)) ac.setCheckable(True) ac.setChecked(view.column_header.sectionsMovable()) diff --git a/src/calibre/gui2/lrf_renderer/text.py b/src/calibre/gui2/lrf_renderer/text.py index 53ab890a58..cef8455882 100644 --- a/src/calibre/gui2/lrf_renderer/text.py +++ b/src/calibre/gui2/lrf_renderer/text.py @@ -172,11 +172,11 @@ class TextBlock: has_content = property(fget=lambda self: self.peek_index < len(self.lines)-1) XML_ENTITIES = { - "apos" : "'", - "quot" : '"', - "amp" : "&", - "lt" : "<", - "gt" : ">" + 'apos' : "'", + 'quot' : '"', + 'amp' : '&', + 'lt' : '<', + 'gt' : '>' } def __init__(self, tb, font_loader, respect_max_y, text_width, logger, diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index 7f99a9f0f8..7166f6d792 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -92,7 +92,7 @@ def find_portable_library(): if len(lib) > 74: error_dialog(None, _('Path too long'), - _("Path to Calibre Portable (%s) " + _('Path to Calibre Portable (%s) ' 'too long. It must be less than 59 characters.')%base, show=True) raise AbortInit() diff --git a/src/calibre/gui2/markdown_editor.py b/src/calibre/gui2/markdown_editor.py index 52ff74fa28..33ded1830c 100644 --- a/src/calibre/gui2/markdown_editor.py +++ b/src/calibre/gui2/markdown_editor.py @@ -45,8 +45,8 @@ class MarkdownEditDialog(QDialog): def __init__(self, parent, text, column_name=None, base_url=None): QDialog.__init__(self, parent) - self.setObjectName("MarkdownEditDialog") - self.setWindowTitle(_("Edit Markdown")) + self.setObjectName('MarkdownEditDialog') + self.setWindowTitle(_('Edit Markdown')) self.verticalLayout = l = QVBoxLayout(self) self.textbox = editor = Editor(self) editor.set_base_url(base_url) diff --git a/src/calibre/gui2/markdown_syntax_highlighter.py b/src/calibre/gui2/markdown_syntax_highlighter.py index 3a44db07b1..696a55d2b3 100644 --- a/src/calibre/gui2/markdown_syntax_highlighter.py +++ b/src/calibre/gui2/markdown_syntax_highlighter.py @@ -35,60 +35,60 @@ class MarkdownHighlighter(QSyntaxHighlighter): } key_theme_maps = { - 'Bold': "bold", - 'Italic': "emphasis", - 'BoldItalic': "boldemphasis", - 'uBold': "bold", - 'uItalic': "emphasis", - 'uBoldItalic': "boldemphasis", - 'Link': "link", - 'Image': "image", - 'LinkRef': "link", - 'Header': "header", - 'HeaderLine': "header", - 'CodeBlock': "codeblock", - 'UnorderedList': "unorderedlist", - 'UnorderedListStar': "unorderedlist", - 'OrderedList': "orderedlist", - 'BlockQuote': "blockquote", - 'CodeSpan': "codespan", - 'HR': "line", - 'Html': "html", - 'Entity': "entity", + 'Bold': 'bold', + 'Italic': 'emphasis', + 'BoldItalic': 'boldemphasis', + 'uBold': 'bold', + 'uItalic': 'emphasis', + 'uBoldItalic': 'boldemphasis', + 'Link': 'link', + 'Image': 'image', + 'LinkRef': 'link', + 'Header': 'header', + 'HeaderLine': 'header', + 'CodeBlock': 'codeblock', + 'UnorderedList': 'unorderedlist', + 'UnorderedListStar': 'unorderedlist', + 'OrderedList': 'orderedlist', + 'BlockQuote': 'blockquote', + 'CodeSpan': 'codespan', + 'HR': 'line', + 'Html': 'html', + 'Entity': 'entity', } light_theme = { - "bold": {"font-weight":"bold"}, - "emphasis": {"font-style":"italic"}, - "boldemphasis": {"font-weight":"bold", "font-style":"italic"}, - "link": {"color":light_link_color.name(), "font-weight":"normal", "font-style":"normal"}, - "image": {"color":"#cb4b16", "font-weight":"normal", "font-style":"normal"}, - "header": {"color":"#2aa198", "font-weight":"bold", "font-style":"normal"}, - "unorderedlist": {"color":"red", "font-weight":"normal", "font-style":"normal"}, - "orderedlist": {"color":"red", "font-weight":"normal", "font-style":"normal"}, - "blockquote": {"color":"red", "font-weight":"bold", "font-style":"normal"}, - "codespan": {"color":"#ff5800", "font-weight":"normal", "font-style":"normal"}, - "codeblock": {"color":"#ff5800", "font-weight":"normal", "font-style":"normal"}, - "line": {"color":"#2aa198", "font-weight":"normal", "font-style":"normal"}, - "html": {"color":"#c000c0", "font-weight":"normal", "font-style":"normal"}, - "entity": {"color":"#006496"}, + 'bold': {'font-weight':'bold'}, + 'emphasis': {'font-style':'italic'}, + 'boldemphasis': {'font-weight':'bold', 'font-style':'italic'}, + 'link': {'color':light_link_color.name(), 'font-weight':'normal', 'font-style':'normal'}, + 'image': {'color':'#cb4b16', 'font-weight':'normal', 'font-style':'normal'}, + 'header': {'color':'#2aa198', 'font-weight':'bold', 'font-style':'normal'}, + 'unorderedlist': {'color':'red', 'font-weight':'normal', 'font-style':'normal'}, + 'orderedlist': {'color':'red', 'font-weight':'normal', 'font-style':'normal'}, + 'blockquote': {'color':'red', 'font-weight':'bold', 'font-style':'normal'}, + 'codespan': {'color':'#ff5800', 'font-weight':'normal', 'font-style':'normal'}, + 'codeblock': {'color':'#ff5800', 'font-weight':'normal', 'font-style':'normal'}, + 'line': {'color':'#2aa198', 'font-weight':'normal', 'font-style':'normal'}, + 'html': {'color':'#c000c0', 'font-weight':'normal', 'font-style':'normal'}, + 'entity': {'color':'#006496'}, } dark_theme = { - "bold": {"font-weight":"bold"}, - "emphasis": {"font-style":"italic"}, - "boldemphasis": {"font-weight":"bold", "font-style":"italic"}, - "link": {"color":dark_link_color.name(), "font-weight":"normal", "font-style":"normal"}, - "image": {"color":"#cb4b16", "font-weight":"normal", "font-style":"normal"}, - "header": {"color":"#2aa198", "font-weight":"bold", "font-style":"normal"}, - "unorderedlist": {"color":"yellow", "font-weight":"normal", "font-style":"normal"}, - "orderedlist": {"color":"yellow", "font-weight":"normal", "font-style":"normal"}, - "blockquote": {"color":"yellow", "font-weight":"bold", "font-style":"normal"}, - "codespan": {"color":"#90ee90", "font-weight":"normal", "font-style":"normal"}, - "codeblock": {"color":"#ff9900", "font-weight":"normal", "font-style":"normal"}, - "line": {"color":"#2aa198", "font-weight":"normal", "font-style":"normal"}, - "html": {"color":"#f653a6", "font-weight":"normal", "font-style":"normal"}, - "entity": {"color":"#ff82ac"}, + 'bold': {'font-weight':'bold'}, + 'emphasis': {'font-style':'italic'}, + 'boldemphasis': {'font-weight':'bold', 'font-style':'italic'}, + 'link': {'color':dark_link_color.name(), 'font-weight':'normal', 'font-style':'normal'}, + 'image': {'color':'#cb4b16', 'font-weight':'normal', 'font-style':'normal'}, + 'header': {'color':'#2aa198', 'font-weight':'bold', 'font-style':'normal'}, + 'unorderedlist': {'color':'yellow', 'font-weight':'normal', 'font-style':'normal'}, + 'orderedlist': {'color':'yellow', 'font-weight':'normal', 'font-style':'normal'}, + 'blockquote': {'color':'yellow', 'font-weight':'bold', 'font-style':'normal'}, + 'codespan': {'color':'#90ee90', 'font-weight':'normal', 'font-style':'normal'}, + 'codeblock': {'color':'#ff9900', 'font-weight':'normal', 'font-style':'normal'}, + 'line': {'color':'#2aa198', 'font-weight':'normal', 'font-style':'normal'}, + 'html': {'color':'#f653a6', 'font-weight':'normal', 'font-style':'normal'}, + 'entity': {'color':'#ff82ac'}, } def __init__(self, parent): diff --git a/src/calibre/gui2/metadata/basic_widgets.py b/src/calibre/gui2/metadata/basic_widgets.py index dd0ccacd88..f3ea2dad74 100644 --- a/src/calibre/gui2/metadata/basic_widgets.py +++ b/src/calibre/gui2/metadata/basic_widgets.py @@ -407,7 +407,7 @@ class AuthorsEdit(EditWithComplete, ToMetadataMixin): try: self.commit(self.db, self.id_) except OSError as e: - e.locking_violation_msg = _('Could not change on-disk location of this book\'s files.') + e.locking_violation_msg = _("Could not change on-disk location of this book's files.") raise self.db.commit() self.original_val = self.current_val @@ -479,7 +479,7 @@ class AuthorSortEdit(EnLineEdit, ToMetadataMixin, LineEditIndicators): TOOLTIP = _('Specify how the author(s) of this book should be sorted. ' 'For example Charles Dickens should be sorted as Dickens, ' 'Charles.\nIf the box is colored green, then text matches ' - 'the individual author\'s sort strings. If it is colored ' + "the individual author's sort strings. If it is colored " 'red, then the authors and this text do not match.') LABEL = _('Author s&ort:') FIELD_NAME = 'author_sort' @@ -1034,7 +1034,7 @@ class FormatsManager(QWidget): def add_format(self, *args): files = choose_files_and_remember_all_files( - self, 'add formats dialog', _("Choose formats for ") + self.dialog.title.current_val, + self, 'add formats dialog', _('Choose formats for ') + self.dialog.title.current_val, [(_('Books'), BOOK_EXTENSIONS)]) self._add_formats(files) @@ -1202,7 +1202,7 @@ class Cover(ImageView): # {{{ self.select_cover_button = CB(_('&Browse'), 'document_open.png', self.select_cover) self.trim_cover_button = b = CB(_('Trim bord&ers'), 'trim.png') b.setToolTip(_( - 'Automatically detect and remove extra space at the cover\'s edges.\n' + "Automatically detect and remove extra space at the cover's edges.\n" 'Pressing it repeatedly can sometimes remove stubborn borders.')) b.m = m = QMenu(b) b.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup) @@ -1282,12 +1282,12 @@ class Cover(ImageView): # {{{ return cover = None try: - with open(_file, "rb") as f: + with open(_file, 'rb') as f: cover = f.read() except OSError as e: d = error_dialog( self, _('Error reading file'), - _("<p>There was an error reading from file: <br /><b>") + _file + "</b></p><br />"+str(e)) + _('<p>There was an error reading from file: <br /><b>') + _file + '</b></p><br />'+str(e)) d.exec() if cover: orig = self.current_val @@ -1295,8 +1295,8 @@ class Cover(ImageView): # {{{ if self.current_val is None: self.current_val = orig error_dialog(self, - _("Not a valid picture"), - _file + _(" is not a valid picture"), show=True) + _('Not a valid picture'), + _file + _(' is not a valid picture'), show=True) def remove_cover(self, *args): self.current_val = None @@ -1616,7 +1616,7 @@ class Identifiers(Dialog): self.l = l = QVBoxLayout(self) self.la = la = QLabel(_( - 'Edit the book\'s identifiers. Every identifier must be on a separate line, and have the form type:value')) + "Edit the book's identifiers. Every identifier must be on a separate line, and have the form type:value")) la.setWordWrap(True) self.text = t = QPlainTextEdit(self) l.addWidget(la), l.addWidget(t) diff --git a/src/calibre/gui2/metadata/single.py b/src/calibre/gui2/metadata/single.py index 88f8ec10a3..f57b5bad2f 100644 --- a/src/calibre/gui2/metadata/single.py +++ b/src/calibre/gui2/metadata/single.py @@ -240,7 +240,7 @@ class MetadataSingleDialogBase(QDialog): self.manage_authors_button.setIcon(QIcon.ic('user_profile.png')) self.manage_authors_button.setToolTip('<p>' + _( 'Open the Manage Authors editor. Use to rename authors and correct ' - 'individual author\'s sort values') + '</p>') + "individual author's sort values") + '</p>') self.manage_authors_button.clicked.connect(self.authors.manage_authors) self.series_editor_button = QToolButton(self) @@ -674,7 +674,7 @@ class MetadataSingleDialogBase(QDialog): widget.commit(self.db, self.book_id) self.books_to_refresh |= getattr(widget, 'books_to_refresh', set()) except OSError as e: - e.locking_violation_msg = _('Could not change on-disk location of this book\'s files.') + e.locking_violation_msg = _("Could not change on-disk location of this book's files.") raise for widget in getattr(self, 'custom_metadata_widgets', []): self.books_to_refresh |= widget.commit(self.book_id) @@ -841,7 +841,7 @@ class MetadataSingleDialog(MetadataSingleDialogBase): # {{{ self.tabs = [] self.labels = [] self.tabs.append(QWidget(self)) - self.central_widget.addTab(ScrollArea(self.tabs[0], self), _("&Basic metadata")) + self.central_widget.addTab(ScrollArea(self.tabs[0], self), _('&Basic metadata')) self.tabs[0].l = l = QVBoxLayout() self.tabs[0].tl = tl = QGridLayout() self.tabs[0].setLayout(l) @@ -1006,12 +1006,12 @@ class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{ self.on_drag_enter.connect(self.handle_drag_enter) self.tabs.append(DragTrackingWidget(self, self.on_drag_enter)) - self.central_widget.addTab(ScrollArea(self.tabs[0], self), _("&Metadata")) + self.central_widget.addTab(ScrollArea(self.tabs[0], self), _('&Metadata')) self.tabs[0].l = QGridLayout() self.tabs[0].setLayout(self.tabs[0].l) self.tabs.append(QWidget(self)) - self.central_widget.addTab(ScrollArea(self.tabs[1], self), _("&Cover and formats")) + self.central_widget.addTab(ScrollArea(self.tabs[1], self), _('&Cover and formats')) self.tabs[1].l = QGridLayout() self.tabs[1].setLayout(self.tabs[1].l) @@ -1166,7 +1166,7 @@ class MetadataSingleDialogAlt2(MetadataSingleDialogBase): # {{{ main_splitter = self.main_splitter = Splitter(Qt.Orientation.Horizontal, self) self.central_widget.tabBar().setVisible(False) - self.central_widget.addTab(ScrollArea(main_splitter, self), _("&Metadata")) + self.central_widget.addTab(ScrollArea(main_splitter, self), _('&Metadata')) # Left side (metadata & comments) # basic and custom split from comments diff --git a/src/calibre/gui2/metadata/single_download.py b/src/calibre/gui2/metadata/single_download.py index 955125dfc2..e35399b5ea 100644 --- a/src/calibre/gui2/metadata/single_download.py +++ b/src/calibre/gui2/metadata/single_download.py @@ -558,7 +558,7 @@ class IdentifyWidget(QWidget): # {{{ error_dialog(self, _('No matches found'), '<p>' + _('Failed to find any books that ' 'match your search. Try making the search <b>less ' - 'specific</b>. For example, use only the author\'s ' + "specific</b>. For example, use only the author's " 'last name and a single distinctive word from ' 'the title.<p>To see the full log, click "Show details".'), show=True, det_msg=log) diff --git a/src/calibre/gui2/notify.py b/src/calibre/gui2/notify.py index 763a371136..0754067cb4 100644 --- a/src/calibre/gui2/notify.py +++ b/src/calibre/gui2/notify.py @@ -125,12 +125,12 @@ class DBUSNotifier(Notifier): self.address, 'AddNotification', 'sa{sv}', ( str(replaces_id or 0), { - "title": ('s', summary), - "body": ('s', body), - "icon": ( + 'title': ('s', summary), + 'body': ('s', body), + 'icon': ( '(sv)', ( - "bytes", + 'bytes', ('ay', icon(data=True)) ) ), @@ -275,7 +275,7 @@ def hello(): def develop_win(): from calibre_extensions.wintoast import initialize_toast, notify initialize_toast(__appname__, MAIN_APP_UID) - notify("calibre notification", "hello world", icon()) + notify('calibre notification', 'hello world', icon()) if __name__ == '__main__': hello() diff --git a/src/calibre/gui2/preferences/columns.py b/src/calibre/gui2/preferences/columns.py index aa4f661eda..0ad9242d38 100644 --- a/src/calibre/gui2/preferences/columns.py +++ b/src/calibre/gui2/preferences/columns.py @@ -248,7 +248,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): item.setFlags(flags) self.opt_columns.setItem(row, self.TYPE_COLUMN, item) - desc = cc['display'].get('description', "") + desc = cc['display'].get('description', '') item = QTableWidgetItem(desc) item.setToolTip(desc) item.setFlags(flags) diff --git a/src/calibre/gui2/preferences/create_custom_column.py b/src/calibre/gui2/preferences/create_custom_column.py index c04471609d..77df94d50a 100644 --- a/src/calibre/gui2/preferences/create_custom_column.py +++ b/src/calibre/gui2/preferences/create_custom_column.py @@ -285,7 +285,7 @@ class CreateCustomColumn(QDialog): for col, name in [('isbn', _('ISBN')), ('formats', _('Formats')), ('yesno', _('Yes/No')), ('tags', _('Tags')), ('series', ngettext('Series', 'Series', 1)), ('rating', - _('Rating')), ('people', _("Names")), ('text', _('Short text'))]: + _('Rating')), ('people', _('Names')), ('text', _('Short text'))]: text += ' <a href="col:%s">%s</a>,'%(col, name) text = text[:-1] s.setText(text) @@ -327,27 +327,27 @@ class CreateCustomColumn(QDialog): # Lookup name self.column_name_box = cnb = QLineEdit(self) - cnb.setToolTip(_("Used for searching the column. Must contain only digits and lower case letters.")) - add_row(_("&Lookup name:"), cnb) + cnb.setToolTip(_('Used for searching the column. Must contain only digits and lower case letters.')) + add_row(_('&Lookup name:'), cnb) # Heading self.column_heading_box = chb = QLineEdit(self) - chb.setToolTip(_("Column heading in the library view and category name in the Tag browser")) - add_row(_("Column &heading:"), chb) + chb.setToolTip(_('Column heading in the library view and category name in the Tag browser')) + add_row(_('Column &heading:'), chb) # Column Type h = QHBoxLayout() self.column_type_box = ctb = QComboBox(self) ctb.setMinimumWidth(70) - ctb.setToolTip(_("What kind of information will be kept in the column.")) + ctb.setToolTip(_('What kind of information will be kept in the column.')) h.addWidget(ctb) - self.use_decorations = ud = QCheckBox(_("Show &checkmarks"), self) + self.use_decorations = ud = QCheckBox(_('Show &checkmarks'), self) ud.setToolTip(_("Show check marks in the GUI. Values of 'yes', 'checked', and 'true'\n" "will show a green check. Values of 'no', 'unchecked', and 'false' will show a red X.\n" "Everything else will show nothing. Note that the values of 'true' and 'false' don't\n" "follow calibre's language settings and are always in English.")) h.addWidget(ud) - self.is_names = ins = QCheckBox(_("Contains names"), self) + self.is_names = ins = QCheckBox(_('Contains names'), self) ins.setToolTip('<p>' + _('Check this box if this column contains names, ' 'like the authors column. If checked, the item separator will be an ampersand ' '(&) instead of a comma (,), sorting will be done using a computed value ' @@ -355,12 +355,12 @@ class CreateCustomColumn(QDialog): 'Lastname" into "Lastname, Firstname"), and item order will be ' 'preserved.')+'</p>') h.addWidget(ins) - add_row(_("&Column type:"), h) + add_row(_('&Column type:'), h) # Description self.description_box = d = QLineEdit(self) - d.setToolTip(_("Optional text describing what this column is for")) - add_row(_("D&escription:"), d) + d.setToolTip(_('Optional text describing what this column is for')) + add_row(_('D&escription:'), d) # bool formatting h1 = QHBoxLayout() @@ -408,12 +408,12 @@ class CreateCustomColumn(QDialog): # Template self.composite_box = cb = TemplateLineEditor(self) - self.composite_default_label = cdl = QLabel(_("Default: (nothing)")) - cb.setToolTip(_("Field template. Uses the same syntax as save templates.")) - cdl.setToolTip(_("Similar to save templates. For example, %s") % "{title} {isbn}") + self.composite_default_label = cdl = QLabel(_('Default: (nothing)')) + cb.setToolTip(_('Field template. Uses the same syntax as save templates.')) + cdl.setToolTip(_('Similar to save templates. For example, %s') % '{title} {isbn}') h = QHBoxLayout() h.addWidget(cb), h.addWidget(cdl) - self.composite_label = add_row(_("&Template:"), h) + self.composite_label = add_row(_('&Template:'), h) # Comments properties self.comments_heading_position = ct = QComboBox(self) @@ -450,10 +450,10 @@ class CreateCustomColumn(QDialog): "A comma-separated list of permitted values. The empty value is always\n" "included, and is the default. For example, the list 'one,two,three' has\n" "four values, the first of them being the empty value.")) - self.enum_default_label = add_row(_("&Values:"), eb) + self.enum_default_label = add_row(_('&Values:'), eb) self.enum_colors = ec = QLineEdit(self) - ec.setToolTip(_("A list of color names to use when displaying an item. The\n" - "list must be empty or contain a color for each value.")) + ec.setToolTip(_('A list of color names to use when displaying an item. The\n' + 'list must be empty or contain a color for each value.')) self.enum_colors_label = add_row(_('Colors:'), ec) # Rating allow half stars @@ -463,14 +463,14 @@ class CreateCustomColumn(QDialog): # Composite display properties l = QHBoxLayout() - self.composite_sort_by_label = la = QLabel(_("&Sort/search column by")) + self.composite_sort_by_label = la = QLabel(_('&Sort/search column by')) self.composite_sort_by = csb = QComboBox(self) - la.setBuddy(csb), csb.setToolTip(_("How this column should handled in the GUI when sorting and searching")) + la.setBuddy(csb), csb.setToolTip(_('How this column should handled in the GUI when sorting and searching')) l.addWidget(la), l.addWidget(csb) - self.composite_make_category = cmc = QCheckBox(_("Show in Tag browser")) - cmc.setToolTip(_("If checked, this column will appear in the Tag browser as a category")) + self.composite_make_category = cmc = QCheckBox(_('Show in Tag browser')) + cmc.setToolTip(_('If checked, this column will appear in the Tag browser as a category')) l.addWidget(cmc) - self.composite_contains_html = cch = QCheckBox(_("Show as HTML in Book details")) + self.composite_contains_html = cch = QCheckBox(_('Show as HTML in Book details')) cch.setToolTip('<p>' + _( 'If checked, this column will be displayed as HTML in ' 'Book details and the Content server. This can be used to ' @@ -488,7 +488,7 @@ class CreateCustomColumn(QDialog): l.addStretch() add_row(None, l) l = QHBoxLayout() - self.composite_in_comments_box = cmc = QCheckBox(_("Show with comments in Book details")) + self.composite_in_comments_box = cmc = QCheckBox(_('Show with comments in Book details')) cmc.setToolTip('<p>' + _('If you check this box then the column contents ' 'will show in the Comments section in the Book details. ' 'You can indicate whether not to have a header or ' @@ -595,11 +595,11 @@ class CreateCustomColumn(QDialog): '<li>AP : use a 12-hour clock instead of a 24-hour clock, with "AP" replaced by the localized string for AM or PM</li>' '<li>iso : the date with time and timezone. Must be the only format present</li>' '</ul></p>' - "<p>For example:\n" - "<ul>\n" - "<li>ddd, d MMM yyyy gives Mon, 5 Jan 2010</li>\n" - "<li>dd MMMM yy gives 05 January 10</li>\n" - "</ul> ")) + '<p>For example:\n' + '<ul>\n' + '<li>ddd, d MMM yyyy gives Mon, 5 Jan 2010</li>\n' + '<li>dd MMMM yy gives 05 January 10</li>\n' + '</ul> ')) else: l, dl = _('&Format for numbers:'), ( '<p>' + _('Default: Not formatted. For format language details see' @@ -745,7 +745,7 @@ class CreateCustomColumn(QDialog): return self.simple_error('', _('The colors box must be empty or ' 'contain the same number of items as the value box')) for tc in c: - if tc not in QColor.colorNames() and not re.match("#(?:[0-9a-f]{3}){1,4}",tc,re.I): + if tc not in QColor.colorNames() and not re.match('#(?:[0-9a-f]{3}){1,4}',tc,re.I): return self.simple_error('', _('The color {0} is unknown').format(tc)) display_dict = {'enum_values': l, 'enum_colors': c} if default_val: @@ -969,15 +969,15 @@ class CreateNewCustomColumn: def create_column(self, lookup_name, column_heading, datatype, is_multiple, display={}, generate_unused_lookup_name=False, freeze_lookup_name=True): - """ See the class documentation for more information.""" + ''' See the class documentation for more information.''' if self.restart_required: - return (self.Result.MUST_RESTART, _("You must restart calibre before making any more changes")) + return (self.Result.MUST_RESTART, _('You must restart calibre before making any more changes')) if not lookup_name.startswith('#'): return (self.Result.INVALID_KEY, _("The lookup name must begin with a '#'")) suffix_number = 1 if lookup_name in self.custcols: if not generate_unused_lookup_name: - return (self.Result.DUPLICATE_KEY, _("The custom column %s already exists") % lookup_name) + return (self.Result.DUPLICATE_KEY, _('The custom column %s already exists') % lookup_name) for suffix_number in range(suffix_number, 100000): nk = '%s_%d'%(lookup_name, suffix_number) if nk not in self.custcols: @@ -988,7 +988,7 @@ class CreateNewCustomColumn: if column_heading in headings: if not generate_unused_lookup_name: return (self.Result.DUPLICATE_HEADING, - _("The column heading %s already exists") % column_heading) + _('The column heading %s already exists') % column_heading) for i in range(suffix_number, 100000): nh = '%s_%d'%(column_heading, i) if nh not in headings: @@ -1001,10 +1001,10 @@ class CreateNewCustomColumn: _("The custom column type %s doesn't exist") % datatype) if is_multiple and '*' + datatype not in CreateCustomColumn.column_types_map: return (self.Result.INVALID_IS_MULTIPLE, - _("You cannot specify is_multiple for the datatype %s") % datatype) + _('You cannot specify is_multiple for the datatype %s') % datatype) if not isinstance(display, dict): return (self.Result.INVALID_DISPLAY, - _("The display parameter must be a Python dictionary")) + _('The display parameter must be a Python dictionary')) self.created_count += 1 self.custcols[lookup_name] = { 'label': lookup_name, @@ -1092,5 +1092,5 @@ class CreateNewCustomColumn: return {v['name']:('#' + v['label']) for v in self.custcols.values()} def must_restart(self): - """Return true if calibre must be restarted before new columns can be added.""" + '''Return true if calibre must be restarted before new columns can be added.''' return self.restart_required diff --git a/src/calibre/gui2/preferences/plugins.py b/src/calibre/gui2/preferences/plugins.py index 8719eefbc8..0e013456a2 100644 --- a/src/calibre/gui2/preferences/plugins.py +++ b/src/calibre/gui2/preferences/plugins.py @@ -230,7 +230,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self._plugin_model = PluginModel(self.user_installed_plugins.isChecked()) self.plugin_view.setModel(self._plugin_model) self.plugin_view.setStyleSheet( - "QTreeView::item { padding-bottom: 10px;}") + 'QTreeView::item { padding-bottom: 10px;}') self.plugin_view.doubleClicked.connect(self.double_clicked) self.toggle_plugin_button.clicked.connect(self.toggle_plugin) self.customize_plugin_button.clicked.connect(self.customize_plugin) diff --git a/src/calibre/gui2/preferences/server.py b/src/calibre/gui2/preferences/server.py index 2aeaeac5c5..497b6c8a05 100644 --- a/src/calibre/gui2/preferences/server.py +++ b/src/calibre/gui2/preferences/server.py @@ -69,7 +69,7 @@ if iswindows and not isportable: def startup_shortcut_path(): startup_path = winutil.special_folder_path(winutil.CSIDL_STARTUP) - return os.path.join(startup_path, "calibre.lnk") + return os.path.join(startup_path, 'calibre.lnk') def create_shortcut(shortcut_path, target, description, *args): quoted_args = None @@ -210,7 +210,7 @@ class Path(QWidget): self.b = b = QToolButton(self) l.addWidget(b) b.setIcon(QIcon.ic('document_open.png')) - b.setToolTip(_("Browse for the file")) + b.setToolTip(_('Browse for the file')) b.clicked.connect(self.choose) init_opt(self, opt, layout) diff --git a/src/calibre/gui2/preferences/tweaks.py b/src/calibre/gui2/preferences/tweaks.py index a0f1e17a70..04deb444af 100644 --- a/src/calibre/gui2/preferences/tweaks.py +++ b/src/calibre/gui2/preferences/tweaks.py @@ -85,7 +85,7 @@ class Tweak: # {{{ self.doc = ' ' + self.doc self.var_names = var_names if self.var_names: - self.doc = "%s: %s\n\n%s"%(_('ID'), self.var_names[0], format_doc(self.doc)) + self.doc = '%s: %s\n\n%s'%(_('ID'), self.var_names[0], format_doc(self.doc)) self.default_values = OrderedDict() for x in var_names: self.default_values[x] = defaults[x] @@ -386,8 +386,8 @@ class ConfigWidget(ConfigWidgetBase): def setupUi(self, x): self.l = l = QVBoxLayout(self) self.la1 = la = QLabel( - _("Values for the tweaks are shown below. Edit them to change the behavior of calibre." - " Your changes will only take effect <b>after a restart</b> of calibre.")) + _('Values for the tweaks are shown below. Edit them to change the behavior of calibre.' + ' Your changes will only take effect <b>after a restart</b> of calibre.')) l.addWidget(la), la.setWordWrap(True) self.splitter = s = QSplitter(self) s.setChildrenCollapsible(False) @@ -399,8 +399,8 @@ class ConfigWidget(ConfigWidgetBase): self.tweaks_view = tv = TweaksView(self) l2.addWidget(tv) self.plugin_tweaks_button = b = QPushButton(self) - b.setToolTip(_("Edit tweaks for any custom plugins you have installed")) - b.setText(_("&Plugin tweaks")) + b.setToolTip(_('Edit tweaks for any custom plugins you have installed')) + b.setText(_('&Plugin tweaks')) l2.addWidget(b) s.addWidget(lv) @@ -416,16 +416,16 @@ class ConfigWidget(ConfigWidgetBase): g.setColumnStretch(0, 100) g.addWidget(self.search, 0, 0, 1, 1) self.next_button = b = QPushButton(self) - b.setIcon(QIcon.ic("arrow-down.png")) - b.setText(_("&Next")) + b.setIcon(QIcon.ic('arrow-down.png')) + b.setText(_('&Next')) g.addWidget(self.next_button, 0, 1, 1, 1) self.previous_button = b = QPushButton(self) - b.setIcon(QIcon.ic("arrow-up.png")) - b.setText(_("&Previous")) + b.setIcon(QIcon.ic('arrow-up.png')) + b.setText(_('&Previous')) g.addWidget(self.previous_button, 0, 2, 1, 1) self.hb = hb = QGroupBox(self) - hb.setTitle(_("Help")) + hb.setTitle(_('Help')) hb.l = l2 = QVBoxLayout(hb) self.help = h = QPlainTextEdit(self) l2.addWidget(h) @@ -434,19 +434,19 @@ class ConfigWidget(ConfigWidgetBase): self.eb = eb = QGroupBox(self) g.addWidget(eb, 2, 0, 1, 3) - eb.setTitle(_("Edit tweak")) + eb.setTitle(_('Edit tweak')) eb.g = ebg = QGridLayout(eb) self.edit_tweak = et = QPlainTextEdit(self) et.setMinimumWidth(400) et.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap) ebg.addWidget(et, 0, 0, 1, 2) self.restore_default_button = b = QPushButton(self) - b.setToolTip(_("Restore this tweak to its default value")) - b.setText(_("&Reset this tweak")) + b.setToolTip(_('Restore this tweak to its default value')) + b.setText(_('&Reset this tweak')) ebg.addWidget(b, 1, 0, 1, 1) self.apply_button = ab = QPushButton(self) - ab.setToolTip(_("Apply any changes you made to this tweak")) - ab.setText(_("&Apply changes to this tweak")) + ab.setToolTip(_('Apply any changes you made to this tweak')) + ab.setText(_('&Apply changes to this tweak')) ebg.addWidget(ab, 1, 1, 1, 1) def genesis(self, gui): @@ -476,7 +476,7 @@ class ConfigWidget(ConfigWidgetBase): self.context_menu.addAction(self.copy_icon, _('Copy to clipboard'), partial(self.copy_item_to_clipboard, - val="%s (%s: %s)"%(tweak.name, + val='%s (%s: %s)'%(tweak.name, _('ID'), tweak.var_names[0]))) self.context_menu.popup(self.mapToGlobal(point)) diff --git a/src/calibre/gui2/proceed.py b/src/calibre/gui2/proceed.py index 532df23744..3c9f4e6a33 100644 --- a/src/calibre/gui2/proceed.py +++ b/src/calibre/gui2/proceed.py @@ -71,7 +71,7 @@ class Icon(QWidget): self.set_icon('dialog_question.png') self.default_icon = self.icon self._fraction = 0.0 - self.animation = a = QPropertyAnimation(self, b"fraction", self) + self.animation = a = QPropertyAnimation(self, b'fraction', self) a.setDuration(2000), a.setEasingCurve(QEasingCurve.Type.Linear) a.setStartValue(0.0), a.setEndValue(2.0), a.setLoopCount(10) @@ -123,7 +123,7 @@ class ProceedQuestion(QWidget): parent.installEventFilter(self) self._show_fraction = 0.0 - self.show_animation = a = QPropertyAnimation(self, b"show_fraction", self) + self.show_animation = a = QPropertyAnimation(self, b'show_fraction', self) a.setDuration(1000), a.setEasingCurve(QEasingCurve.Type.OutQuad) a.setStartValue(0.0), a.setEndValue(1.0) a.finished.connect(self.stop_show_animation) diff --git a/src/calibre/gui2/qt_file_dialogs.py b/src/calibre/gui2/qt_file_dialogs.py index da27487567..997f7d5e59 100644 --- a/src/calibre/gui2/qt_file_dialogs.py +++ b/src/calibre/gui2/qt_file_dialogs.py @@ -129,21 +129,21 @@ class FileDialog(QObject): opts |= QFileDialog.Option.HideNameFilterDetails if mode == QFileDialog.FileMode.AnyFile: if use_native_dialog: - f = QFileDialog.getSaveFileName(parent, title, initial_dir, ftext, "", opts) + f = QFileDialog.getSaveFileName(parent, title, initial_dir, ftext, '', opts) else: f = create_dialog(title, ftext, for_saving=True) if f and f[0]: self.selected_files.append(f[0]) elif mode == QFileDialog.FileMode.ExistingFile: if use_native_dialog: - f = QFileDialog.getOpenFileName(parent, title, initial_dir, ftext, "", opts) + f = QFileDialog.getOpenFileName(parent, title, initial_dir, ftext, '', opts) else: f = create_dialog(title, ftext) if f and f[0] and os.path.exists(f[0]): self.selected_files.append(f[0]) elif mode == QFileDialog.FileMode.ExistingFiles: if use_native_dialog: - fs = QFileDialog.getOpenFileNames(parent, title, initial_dir, ftext, "", opts) + fs = QFileDialog.getOpenFileNames(parent, title, initial_dir, ftext, '', opts) else: fs = create_dialog(title, ftext), True if fs and fs[0]: diff --git a/src/calibre/gui2/search_restriction_mixin.py b/src/calibre/gui2/search_restriction_mixin.py index b91aed7e64..942a79a943 100644 --- a/src/calibre/gui2/search_restriction_mixin.py +++ b/src/calibre/gui2/search_restriction_mixin.py @@ -375,7 +375,7 @@ class SearchRestrictionMixin: self.search_restriction = ComboBoxWithHelp(self) self.search_restriction.setVisible(False) - self.clear_vl.setText(_("(all books)")) + self.clear_vl.setText(_('(all books)')) self.ar_menu = QMenu(_('Additional restriction'), self.virtual_library_menu) self.edit_menu = QMenu(_('Edit Virtual library'), self.virtual_library_menu) self.rm_menu = QMenu(_('Remove Virtual library'), self.virtual_library_menu) diff --git a/src/calibre/gui2/splash_screen.py b/src/calibre/gui2/splash_screen.py index b7600e32fa..5ee7306794 100644 --- a/src/calibre/gui2/splash_screen.py +++ b/src/calibre/gui2/splash_screen.py @@ -77,7 +77,7 @@ class SplashScreen(QSplashScreen): x = pw + 10 width -= num_width + 5 + x painter.setFont(self.title_font) - painter.drawText(x, y, width, self.title_height, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter | Qt.TextFlag.TextSingleLine, "CALIBRE") + painter.drawText(x, y, width, self.title_height, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter | Qt.TextFlag.TextSingleLine, 'CALIBRE') # Draw starting up message y += self.title_height + 5 diff --git a/src/calibre/gui2/store/search/search.py b/src/calibre/gui2/store/search/search.py index dea3ad8a31..55bdb2f5e8 100644 --- a/src/calibre/gui2/store/search/search.py +++ b/src/calibre/gui2/store/search/search.py @@ -198,7 +198,7 @@ class SearchDialog(QDialog, Ui_Dialog): query.append('author2:"%s"' % str(self.search_author.text()).replace('"', ' ')) if self.search_edit.text(): query.append(str(self.search_edit.text())) - query = " ".join(query) + query = ' '.join(query) if not query.strip(): error_dialog(self, _('No query'), _('You must enter a title, author or keyword to' @@ -385,7 +385,7 @@ class SearchDialog(QDialog, Ui_Dialog): self.results_view.model().add_result(res, store_plugin) if not self.search_pool.threads_running() and not self.results_view.model().has_results(): - info_dialog(self, _('No matches'), _('Couldn\'t find any books matching your query.'), show=True, show_copy_button=False) + info_dialog(self, _('No matches'), _("Couldn't find any books matching your query."), show=True, show_copy_button=False) def update_book_total(self, total): self.total.setText('%s' % total) diff --git a/src/calibre/gui2/store/stores/amazon_es_plugin.py b/src/calibre/gui2/store/stores/amazon_es_plugin.py index 64a2620d07..607d5723d3 100644 --- a/src/calibre/gui2/store/stores/amazon_es_plugin.py +++ b/src/calibre/gui2/store/stores/amazon_es_plugin.py @@ -100,7 +100,7 @@ def search_amazon(query, max_results=10, timeout=60, title = ''.join(data.xpath(title_xpath)) author = ''.join(data.xpath(author_xpath)) try: - author = author.split('by ', 1)[1].split(" (")[0] + author = author.split('by ', 1)[1].split(' (')[0] except: pass diff --git a/src/calibre/gui2/store/stores/amazon_it_plugin.py b/src/calibre/gui2/store/stores/amazon_it_plugin.py index c7c2617729..895c825d81 100644 --- a/src/calibre/gui2/store/stores/amazon_it_plugin.py +++ b/src/calibre/gui2/store/stores/amazon_it_plugin.py @@ -100,7 +100,7 @@ def search_amazon(query, max_results=10, timeout=60, title = ''.join(data.xpath(title_xpath)) author = ''.join(data.xpath(author_xpath)) try: - author = author.split('by ', 1)[1].split(" (")[0] + author = author.split('by ', 1)[1].split(' (')[0] except: pass diff --git a/src/calibre/gui2/store/stores/bn_plugin.py b/src/calibre/gui2/store/stores/bn_plugin.py index d4eacf5cbc..efc16d81c0 100644 --- a/src/calibre/gui2/store/stores/bn_plugin.py +++ b/src/calibre/gui2/store/stores/bn_plugin.py @@ -83,7 +83,7 @@ def search_bn(query, max_results=10, timeout=60, write_html_to=''): class BNStore(BasicStoreConfig, StorePlugin): def open(self, parent=None, detail_item=None, external=False): - url = "https://bn.com" + url = 'https://bn.com' if external or self.config.get('open_external', False): open_url(QUrl(url_slash_cleaner(detail_item if detail_item else url))) diff --git a/src/calibre/gui2/store/stores/chitanka_plugin.py b/src/calibre/gui2/store/stores/chitanka_plugin.py index 29fa910f3e..ac32dfe8b7 100644 --- a/src/calibre/gui2/store/stores/chitanka_plugin.py +++ b/src/calibre/gui2/store/stores/chitanka_plugin.py @@ -45,7 +45,7 @@ def parse_book_page(doc, base_url, counter): s.title = ''.join(data.xpath('.//div[@class="media-body"]/a[@class="booklink"]/i/text()')).strip() alternative_headline = data.xpath('.//div[@class="media-body"]/div[@itemprop="alternativeHeadline"]/text()') if len(alternative_headline) > 0: - s.title = "{} ({})".format(s.title, ''.join(alternative_headline).strip()) + s.title = '{} ({})'.format(s.title, ''.join(alternative_headline).strip()) s.author = ', '.join(data.xpath('.//div[@class="media-body"]/div[@class="bookauthor"]/span/a/text()')).strip(', ') s.detail_item = id diff --git a/src/calibre/gui2/store/stores/ebooks_com_plugin.py b/src/calibre/gui2/store/stores/ebooks_com_plugin.py index 06a3973e0e..19bfdccae8 100644 --- a/src/calibre/gui2/store/stores/ebooks_com_plugin.py +++ b/src/calibre/gui2/store/stores/ebooks_com_plugin.py @@ -58,7 +58,7 @@ def search_ec(query, max_results=10, timeout=60, write_html_to=''): s.author = ' & '.join(x['name'] for x in book['authors']) s.price = book['price'] s.detail_item = absolutize(book['book_url']) - s.ebooks_com_api_url = 'https://www.ebooks.com/api/book/?bookId={}&countryCode={}'.format(book["id"], cc) + s.ebooks_com_api_url = 'https://www.ebooks.com/api/book/?bookId={}&countryCode={}'.format(book['id'], cc) s.drm = SearchResult.DRM_UNKNOWN yield s diff --git a/src/calibre/gui2/store/stores/ebookshoppe_uk_plugin.py b/src/calibre/gui2/store/stores/ebookshoppe_uk_plugin.py index d9259366da..ecca17c687 100644 --- a/src/calibre/gui2/store/stores/ebookshoppe_uk_plugin.py +++ b/src/calibre/gui2/store/stores/ebookshoppe_uk_plugin.py @@ -46,7 +46,7 @@ class EBookShoppeUKStore(BasicStoreConfig, StorePlugin): def search(self, query, max_results=10, timeout=60): url = 'http://www.ebookshoppe.com/search.php?search_query=' + quote(query) br = browser() - br.addheaders = [("Referer", "http://www.ebookshoppe.com/")] + br.addheaders = [('Referer', 'http://www.ebookshoppe.com/')] counter = max_results with closing(br.open(url, timeout=timeout)) as f: diff --git a/src/calibre/gui2/store/stores/gutenberg_plugin.py b/src/calibre/gui2/store/stores/gutenberg_plugin.py index f54748d22d..e8e9a04dd5 100644 --- a/src/calibre/gui2/store/stores/gutenberg_plugin.py +++ b/src/calibre/gui2/store/stores/gutenberg_plugin.py @@ -55,7 +55,7 @@ def search(query, max_results=10, timeout=60, write_raw_to=None): try: s.author = etree.tostring(next(CSSSelect('span.subtitle', li)), method='text', encoding='unicode').strip() except StopIteration: - s.author = "" + s.author = '' for img in CSSSelect('img.cover-thumb', li): s.cover_url = absurl(img.get('src')) break diff --git a/src/calibre/gui2/store/stores/litres_plugin.py b/src/calibre/gui2/store/stores/litres_plugin.py index 51a231692c..0a7880d450 100644 --- a/src/calibre/gui2/store/stores/litres_plugin.py +++ b/src/calibre/gui2/store/stores/litres_plugin.py @@ -111,7 +111,7 @@ def format_price_in_RUR(price): @return: formatted price if possible otherwise original value @rtype: unicode ''' - if price and re.match(r"^\d*?\.\d*?$", price): + if price and re.match(r'^\d*?\.\d*?$', price): try: price = u'{:,.2F} \u20BD'.format(float(price)) # \u20BD => руб. price = price.replace(',', ' ').replace('.', ',', 1) diff --git a/src/calibre/gui2/tag_browser/model.py b/src/calibre/gui2/tag_browser/model.py index f4d6b1e352..3622e34b8c 100644 --- a/src/calibre/gui2/tag_browser/model.py +++ b/src/calibre/gui2/tag_browser/model.py @@ -1055,7 +1055,7 @@ class TagsModel(QAbstractItemModel): # {{{ # Drag'n Drop {{{ def mimeTypes(self): - return ["application/calibre+from_library", + return ['application/calibre+from_library', 'application/calibre+from_tag_browser'] def mimeData(self, indexes): @@ -1086,7 +1086,7 @@ class TagsModel(QAbstractItemModel): # {{{ fmts = {str(x) for x in md.formats()} if not fmts.intersection(set(self.mimeTypes())): return False - if "application/calibre+from_library" in fmts: + if 'application/calibre+from_library' in fmts: if action != Qt.DropAction.CopyAction: return False return self.do_drop_from_library(md, action, row, column, parent) @@ -1913,7 +1913,7 @@ class TagsModel(QAbstractItemModel): # {{{ for node in self.category_nodes: if node.tag.state: - if node.category_key == "news": + if node.category_key == 'news': if node_searches[node.tag.state] == 'true': ans.append('tags:"=' + _('News') + '"') else: diff --git a/src/calibre/gui2/tag_browser/ui.py b/src/calibre/gui2/tag_browser/ui.py index 112305a8ef..56759537ed 100644 --- a/src/calibre/gui2/tag_browser/ui.py +++ b/src/calibre/gui2/tag_browser/ui.py @@ -893,7 +893,7 @@ class TagBrowserWidget(QFrame): # {{{ ac = QAction(parent) parent.addAction(ac) parent.keyboard.register_shortcut('tag browser set focus', - _("Give the Tag browser keyboard focus"), default_keys=(), + _('Give the Tag browser keyboard focus'), default_keys=(), action=ac, group=_('Tag browser')) ac.triggered.connect(self.give_tb_focus) diff --git a/src/calibre/gui2/tag_browser/view.py b/src/calibre/gui2/tag_browser/view.py index 1889261957..abb481fb18 100644 --- a/src/calibre/gui2/tag_browser/view.py +++ b/src/calibre/gui2/tag_browser/view.py @@ -300,7 +300,7 @@ class TagsView(QTreeView): # {{{ self.pane_is_visible = False self.current_expansion = None self.search_icon = QIcon.ic('search.png') - self.search_copy_icon = QIcon.ic("search_copy_saved.png") + self.search_copy_icon = QIcon.ic('search_copy_saved.png') self.user_category_icon = QIcon.ic('tb_folder.png') self.edit_metadata_icon = QIcon.ic('edit_input.png') self.delete_icon = QIcon.ic('list_remove.png') @@ -817,7 +817,7 @@ class TagsView(QTreeView): # {{{ if action == 'search': self._toggle(index, set_to=search_state) return - if action == "raw_search": + if action == 'raw_search': get_gui().get_saved_search_text(search_name='search:' + key) return if action == 'add_to_category': @@ -1404,7 +1404,7 @@ class TagsView(QTreeView): # {{{ for p in paths: # Now add the menu items m.addAction(self.minus_icon, - _("Collapse {0}").format(p[0]), partial(self.collapse_node, p[1])) + _('Collapse {0}').format(p[0]), partial(self.collapse_node, p[1])) m.addAction(self.minus_icon, _('Collapse all'), self.collapseAll) # Ask plugins if they have any actions to add to the context menu diff --git a/src/calibre/gui2/toc/location.py b/src/calibre/gui2/toc/location.py index a9d3725da3..df3240f5da 100644 --- a/src/calibre/gui2/toc/location.py +++ b/src/calibre/gui2/toc/location.py @@ -149,7 +149,7 @@ class UrlSchemeHandler(QWebEngineUrlSchemeHandler): if fail_code is None: fail_code = QWebEngineUrlRequestJob.Error.UrlNotFound rq.fail(fail_code) - print(f"Blocking FAKE_PROTOCOL request: {rq.requestUrl().toString()} with code: {fail_code}", file=sys.stderr) + print(f'Blocking FAKE_PROTOCOL request: {rq.requestUrl().toString()} with code: {fail_code}', file=sys.stderr) class Page(QWebEnginePage): # {{{ @@ -309,7 +309,7 @@ class ItemEdit(QWidget): sp.addWidget(f) f.la = la = QLabel('<p>'+_( - 'Here you can choose a destination for the Table of Contents\' entry' + "Here you can choose a destination for the Table of Contents' entry" ' to point to. First choose a file from the book in the left-most panel. The' ' file will open in the central panel.<p>' diff --git a/src/calibre/gui2/tools.py b/src/calibre/gui2/tools.py index 2cdc626af1..4852c7e504 100644 --- a/src/calibre/gui2/tools.py +++ b/src/calibre/gui2/tools.py @@ -110,7 +110,7 @@ def convert_single_ebook(parent, db, book_ids, auto_conversion=False, # {{{ warning_dialog(parent, _('Could not convert'), '<p>'+ _( 'Could not convert <b>%s</b> as it has no e-book files. If you ' 'think it should have files, but calibre is not finding ' - 'them, that is most likely because you moved the book\'s ' + "them, that is most likely because you moved the book's " 'files around outside of calibre. You will need to find those files ' 'and re-add them to calibre.')%title, show=True) else: diff --git a/src/calibre/gui2/tts/types.py b/src/calibre/gui2/tts/types.py index 4f46e22fc4..ce4a4e52fc 100644 --- a/src/calibre/gui2/tts/types.py +++ b/src/calibre/gui2/tts/types.py @@ -247,7 +247,7 @@ def available_engines() -> dict[str, EngineMetadata]: except ImportError: pass else: - cmd = os.getenv("SPEECHD_CMD", SPD_SPAWN_CMD) + cmd = os.getenv('SPEECHD_CMD', SPD_SPAWN_CMD) if cmd and os.access(cmd, os.X_OK) and os.path.isfile(cmd): ans['speechd'] = EngineMetadata('speechd', _('The Speech Dispatcher Engine'), _( 'The "speechd" engine can usually track the currently spoken word on screen, however, it depends on the' diff --git a/src/calibre/gui2/tweak_book/char_select.py b/src/calibre/gui2/tweak_book/char_select.py index 0cdfff965d..93cd726ce3 100644 --- a/src/calibre/gui2/tweak_book/char_select.py +++ b/src/calibre/gui2/tweak_book/char_select.py @@ -140,7 +140,7 @@ class CategoryModel(QAbstractItemModel): (_('Ethiopic extended A'), (0xAB00, 0xAB2F)), (_('Meroitic cursive'), (0x109A0, 0x109FF)), (_('Meroitic hieroglyphs'), (0x10980, 0x1099F)), - (_('N\'Ko'), (0x7C0, 0x7FF)), + (_("N'Ko"), (0x7C0, 0x7FF)), (_('Osmanya'), (0x10480, 0x104AF)), (_('Tifinagh'), (0x2D30, 0x2D7F)), (_('Vai'), (0xA500, 0xA63F)), diff --git a/src/calibre/gui2/tweak_book/diff/main.py b/src/calibre/gui2/tweak_book/diff/main.py index ae9bbd1675..0089ef203e 100644 --- a/src/calibre/gui2/tweak_book/diff/main.py +++ b/src/calibre/gui2/tweak_book/diff/main.py @@ -133,7 +133,7 @@ def get_decoded_raw(name): if syntax in {'html', 'xml'}: raw = xml_to_unicode(raw, verbose=True)[0] else: - m = re.search(br"coding[:=]\s*([-\w.]+)", raw[:1024], flags=re.I) + m = re.search(br'coding[:=]\s*([-\w.]+)', raw[:1024], flags=re.I) if m is not None and m.group(1) != '8bit': enc = m.group(1) if enc == b'unicode': diff --git a/src/calibre/gui2/tweak_book/diff/view.py b/src/calibre/gui2/tweak_book/diff/view.py index 1c3b56fa6f..d7e487dc76 100644 --- a/src/calibre/gui2/tweak_book/diff/view.py +++ b/src/calibre/gui2/tweak_book/diff/view.py @@ -531,7 +531,7 @@ class DiffSplit(QSplitter): # {{{ self.left, self.right = TextBrowser(parent=self), TextBrowser(right=True, parent=self, show_open_in_editor=show_open_in_editor) self.addWidget(self.left), self.addWidget(self.right) - self.split_words = re.compile(r"\w+|\W", re.UNICODE) + self.split_words = re.compile(r'\w+|\W', re.UNICODE) self.clear() def createHandle(self): diff --git a/src/calibre/gui2/tweak_book/editor/syntax/css.py b/src/calibre/gui2/tweak_book/editor/syntax/css.py index 1efd037ac2..edc04c7828 100644 --- a/src/calibre/gui2/tweak_book/editor/syntax/css.py +++ b/src/calibre/gui2/tweak_book/editor/syntax/css.py @@ -163,7 +163,7 @@ class CSSState: return not self.__eq__(other) def __repr__(self): - return f"CSSState(parse={self.parse}, blocks={self.blocks})" + return f'CSSState(parse={self.parse}, blocks={self.blocks})' __str__ = __repr__ diff --git a/src/calibre/gui2/tweak_book/editor/syntax/javascript.py b/src/calibre/gui2/tweak_book/editor/syntax/javascript.py index 3ed167f5fe..aa85f607e6 100644 --- a/src/calibre/gui2/tweak_book/editor/syntax/javascript.py +++ b/src/calibre/gui2/tweak_book/editor/syntax/javascript.py @@ -24,11 +24,11 @@ JS_IDENT = JS_IDENT_START + '(?:' + JS_IDENT_PART + ')*' class JavascriptLexer(RegexLexer): - """ + ''' For JavaScript source code. This is based on the pygments JS highlighter, bu that does not handle multi-line comments in streaming mode, so we had to modify it. - """ + ''' flags = re.UNICODE | re.MULTILINE diff --git a/src/calibre/gui2/tweak_book/editor/syntax/pygments_highlighter.py b/src/calibre/gui2/tweak_book/editor/syntax/pygments_highlighter.py index 7297730750..b85908429f 100644 --- a/src/calibre/gui2/tweak_book/editor/syntax/pygments_highlighter.py +++ b/src/calibre/gui2/tweak_book/editor/syntax/pygments_highlighter.py @@ -52,7 +52,7 @@ def create_lexer(base_class): elif new_state == '#push': statestack.append(statestack[-1]) else: - assert False, "wrong state def: %r" % new_state + assert False, 'wrong state def: %r' % new_state statetokens = tokendefs[statestack[-1]] break else: @@ -119,7 +119,7 @@ class State: return not self.__eq__(other) def __repr__(self): - return "PythonState(%r)" % self.pygments_stack + return 'PythonState(%r)' % self.pygments_stack __str__ = __repr__ diff --git a/src/calibre/gui2/tweak_book/preferences.py b/src/calibre/gui2/tweak_book/preferences.py index d0946fbff3..a532e6dd44 100644 --- a/src/calibre/gui2/tweak_book/preferences.py +++ b/src/calibre/gui2/tweak_book/preferences.py @@ -87,7 +87,7 @@ class BasicSettings(QWidget): # {{{ raise TypeError('Unknown setting type for setting: %s' % name) else: if getter is None or setter is None: - raise ValueError("getter or setter not provided for: %s" % name) + raise ValueError('getter or setter not provided for: %s' % name) self._prevent_changed = True setter(widget, inval) self._prevent_changed = False diff --git a/src/calibre/gui2/tweak_book/preview.py b/src/calibre/gui2/tweak_book/preview.py index 1254b88cd4..98465cb05f 100644 --- a/src/calibre/gui2/tweak_book/preview.py +++ b/src/calibre/gui2/tweak_book/preview.py @@ -139,7 +139,7 @@ class ParseWorker(Thread): pi.parsing_done = True parsed_data = res['result'] if res['tb']: - prints("Parser error:") + prints('Parser error:') prints(res['tb']) else: pi.parsed_data = parsed_data diff --git a/src/calibre/gui2/viewer/lookup.py b/src/calibre/gui2/viewer/lookup.py index 0afc2d4fe6..d550ce83a4 100644 --- a/src/calibre/gui2/viewer/lookup.py +++ b/src/calibre/gui2/viewer/lookup.py @@ -316,7 +316,7 @@ def set_sync_override(allowed): def blank_html(): - msg = _('Double click on a word in the book\'s text to look it up.') + msg = _("Double click on a word in the book's text to look it up.") html = '<p>' + msg app = QApplication.instance() if app.is_dark_theme: diff --git a/src/calibre/gui2/viewer/ui.py b/src/calibre/gui2/viewer/ui.py index 6cd93c9eb6..0e08dc8ab0 100644 --- a/src/calibre/gui2/viewer/ui.py +++ b/src/calibre/gui2/viewer/ui.py @@ -420,21 +420,21 @@ class EbookViewer(MainWindow): self.image_popup() else: error_dialog(self, _('Invalid image'), _( - "Failed to load the image {}").format(name), show=True) + 'Failed to load the image {}').format(name), show=True) else: error_dialog(self, _('Image not found'), _( - "Failed to find the image {}").format(name), show=True) + 'Failed to find the image {}').format(name), show=True) def copy_image(self, name): path = get_path_for_name(name) if not path: return error_dialog(self, _('Image not found'), _( - "Failed to find the image {}").format(name), show=True) + 'Failed to find the image {}').format(name), show=True) try: img = image_from_path(path) except Exception: return error_dialog(self, _('Invalid image'), _( - "Failed to load the image {}").format(name), show=True) + 'Failed to load the image {}').format(name), show=True) url = QUrl.fromLocalFile(path) md = QMimeData() md.setImageData(img) diff --git a/src/calibre/gui2/viewer/web_view.py b/src/calibre/gui2/viewer/web_view.py index 4e64405f6b..b39fc82fcb 100644 --- a/src/calibre/gui2/viewer/web_view.py +++ b/src/calibre/gui2/viewer/web_view.py @@ -140,14 +140,14 @@ def handle_mathjax_request(rq, name): with open(path, 'rb') as f: raw = f.read() except OSError as err: - prints(f"Failed to get mathjax file: {name} with error: {err}", file=sys.stderr) + prints(f'Failed to get mathjax file: {name} with error: {err}', file=sys.stderr) rq.fail(QWebEngineUrlRequestJob.Error.RequestFailed) return if name.endswith('/startup.js'): raw = P('pdf-mathjax-loader.js', data=True, allow_user_override=False) + raw send_reply(rq, mt, raw) else: - prints(f"Failed to get mathjax file: {name} outside mathjax directory", file=sys.stderr) + prints(f'Failed to get mathjax file: {name} outside mathjax directory', file=sys.stderr) rq.fail(QWebEngineUrlRequestJob.Error.RequestFailed) @@ -212,7 +212,7 @@ class UrlSchemeHandler(QWebEngineUrlSchemeHandler): if fail_code is None: fail_code = QWebEngineUrlRequestJob.Error.UrlNotFound rq.fail(fail_code) - prints(f"Blocking FAKE_PROTOCOL request: {rq.requestUrl().toString()} with code: {fail_code}") + prints(f'Blocking FAKE_PROTOCOL request: {rq.requestUrl().toString()} with code: {fail_code}') # }}} diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index 9524571cf3..c6cba20c72 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -918,24 +918,24 @@ class PythonHighlighter(QSyntaxHighlighter): # {{{ Rules = () Formats = {} - KEYWORDS = ["and", "as", "assert", "break", "class", "continue", "def", - "del", "elif", "else", "except", "exec", "finally", "for", "from", - "global", "if", "import", "in", "is", "lambda", "not", "or", - "pass", "print", "raise", "return", "try", "while", "with", - "yield"] + KEYWORDS = ['and', 'as', 'assert', 'break', 'class', 'continue', 'def', + 'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from', + 'global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', + 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', + 'yield'] - BUILTINS = ["abs", "all", "any", "basestring", "bool", "callable", "chr", - "classmethod", "cmp", "compile", "complex", "delattr", "dict", - "dir", "divmod", "enumerate", "eval", "execfile", "exit", "file", - "filter", "float", "frozenset", "getattr", "globals", "hasattr", - "hex", "id", "int", "isinstance", "issubclass", "iter", "len", - "list", "locals", "long", "map", "max", "min", "object", "oct", - "open", "ord", "pow", "property", "range", "reduce", "repr", - "reversed", "round", "set", "setattr", "slice", "sorted", - "staticmethod", "str", "sum", "super", "tuple", "type", "unichr", - "unicode", "vars", "xrange", "zip"] + BUILTINS = ['abs', 'all', 'any', 'basestring', 'bool', 'callable', 'chr', + 'classmethod', 'cmp', 'compile', 'complex', 'delattr', 'dict', + 'dir', 'divmod', 'enumerate', 'eval', 'execfile', 'exit', 'file', + 'filter', 'float', 'frozenset', 'getattr', 'globals', 'hasattr', + 'hex', 'id', 'int', 'isinstance', 'issubclass', 'iter', 'len', + 'list', 'locals', 'long', 'map', 'max', 'min', 'object', 'oct', + 'open', 'ord', 'pow', 'property', 'range', 'reduce', 'repr', + 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', + 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', + 'unicode', 'vars', 'xrange', 'zip'] - CONSTANTS = ["False", "True", "None", "NotImplemented", "Ellipsis"] + CONSTANTS = ['False', 'True', 'None', 'NotImplemented', 'Ellipsis'] def __init__(self, parent=None): super().__init__(parent) @@ -951,26 +951,26 @@ class PythonHighlighter(QSyntaxHighlighter): # {{{ r.append((a, b)) a(re.compile( - "|".join([r"\b%s\b" % keyword for keyword in cls.KEYWORDS])), - "keyword") + '|'.join([r'\b%s\b' % keyword for keyword in cls.KEYWORDS])), + 'keyword') a(re.compile( - "|".join([r"\b%s\b" % builtin for builtin in cls.BUILTINS])), - "builtin") + '|'.join([r'\b%s\b' % builtin for builtin in cls.BUILTINS])), + 'builtin') a(re.compile( - "|".join([r"\b%s\b" % constant - for constant in cls.CONSTANTS])), "constant") + '|'.join([r'\b%s\b' % constant + for constant in cls.CONSTANTS])), 'constant') a(re.compile( - r"\b[+-]?[0-9]+[lL]?\b" - r"|\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b" - r"|\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b"), - "number") + r'\b[+-]?[0-9]+[lL]?\b' + r'|\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b' + r'|\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b'), + 'number') a(re.compile( - r"\bPyQt6\b|\bQt?[A-Z][a-z]\w+\b"), "pyqt") - a(re.compile(r"\b@\w+\b"), "decorator") - stringRe = re.compile(r"""(?:'[^']*?'|"[^"]*?")""") - a(stringRe, "string") + r'\bPyQt6\b|\bQt?[A-Z][a-z]\w+\b'), 'pyqt') + a(re.compile(r'\b@\w+\b'), 'decorator') + stringRe = re.compile(r'''(?:'[^']*?'|"[^"]*?")''') + a(stringRe, 'string') cls.stringRe = re.compile(r"""(:?"["]".*?"["]"|'''.*?''')""") - a(cls.stringRe, "string") + a(cls.stringRe, 'string') cls.tripleSingleRe = re.compile(r"""'''(?!")""") cls.tripleDoubleRe = re.compile(r'''"""(?!')''') cls.Rules = tuple(r) @@ -982,16 +982,16 @@ class PythonHighlighter(QSyntaxHighlighter): # {{{ p = QApplication.instance().palette() is_dark = QApplication.instance().is_dark_theme for name, color, bold, italic in ( - ("normal", None, False, False), - ("keyword", p.color(QPalette.ColorRole.Link).name(), True, False), - ("builtin", p.color(QPalette.ColorRole.Link).name(), False, False), - ("constant", p.color(QPalette.ColorRole.Link).name(), False, False), - ("decorator", "#0000E0", False, False), - ("comment", '#00c700' if is_dark else "#007F00", False, True), - ("string", '#b6b600' if is_dark else "#808000", False, False), - ("number", '#d96d00' if is_dark else "#924900", False, False), - ("error", "#FF0000", False, False), - ("pyqt", "#50621A", False, False)): + ('normal', None, False, False), + ('keyword', p.color(QPalette.ColorRole.Link).name(), True, False), + ('builtin', p.color(QPalette.ColorRole.Link).name(), False, False), + ('constant', p.color(QPalette.ColorRole.Link).name(), False, False), + ('decorator', '#0000E0', False, False), + ('comment', '#00c700' if is_dark else '#007F00', False, True), + ('string', '#b6b600' if is_dark else '#808000', False, False), + ('number', '#d96d00' if is_dark else '#924900', False, False), + ('error', '#FF0000', False, False), + ('pyqt', '#50621A', False, False)): fmt = QTextCharFormat(baseFormat) if color is not None: @@ -1009,18 +1009,18 @@ class PythonHighlighter(QSyntaxHighlighter): # {{{ prevState = self.previousBlockState() self.setFormat(0, textLength, - self.Formats["normal"]) + self.Formats['normal']) - if text.startswith("Traceback") or text.startswith("Error: "): + if text.startswith('Traceback') or text.startswith('Error: '): self.setCurrentBlockState(ERROR) self.setFormat(0, textLength, - self.Formats["error"]) + self.Formats['error']) return if prevState == ERROR and \ - not (text.startswith('>>>') or text.startswith("#")): + not (text.startswith('>>>') or text.startswith('#')): self.setCurrentBlockState(ERROR) self.setFormat(0, textLength, - self.Formats["error"]) + self.Formats['error']) return for regex, fmt in PythonHighlighter.Rules: @@ -1032,8 +1032,8 @@ class PythonHighlighter(QSyntaxHighlighter): # {{{ # PythonHighlighter.Rules.append((re.compile(r"#.*"), "comment")) if not text: pass - elif text[0] == "#": - self.setFormat(0, len(text), self.Formats["comment"]) + elif text[0] == '#': + self.setFormat(0, len(text), self.Formats['comment']) else: stack = [] for i, c in enumerate(text): @@ -1042,8 +1042,8 @@ class PythonHighlighter(QSyntaxHighlighter): # {{{ stack.pop() else: stack.append(c) - elif c == "#" and len(stack) == 0: - self.setFormat(i, len(text), self.Formats["comment"]) + elif c == '#' and len(stack) == 0: + self.setFormat(i, len(text), self.Formats['comment']) break self.setCurrentBlockState(NORMAL) @@ -1061,11 +1061,11 @@ class PythonHighlighter(QSyntaxHighlighter): # {{{ i = len(text) self.setCurrentBlockState(state) self.setFormat(0, i + 3, - self.Formats["string"]) + self.Formats['string']) elif i > -1: self.setCurrentBlockState(state) self.setFormat(i, len(text), - self.Formats["string"]) + self.Formats['string']) def rehighlight(self): QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor)) diff --git a/src/calibre/gui2/win_file_dialogs.py b/src/calibre/gui2/win_file_dialogs.py index 0a7a54b493..ad6d020f28 100644 --- a/src/calibre/gui2/win_file_dialogs.py +++ b/src/calibre/gui2/win_file_dialogs.py @@ -66,7 +66,7 @@ def serialize_string(key, val): def serialize_file_types(file_types): - key = b"FILE_TYPES" + key = b'FILE_TYPES' buf = [struct.pack('=B%dsH' % len(key), len(key), key, len(file_types))] def add(x): diff --git a/src/calibre/gui2/wizard/__init__.py b/src/calibre/gui2/wizard/__init__.py index 07105dee15..d75e771ac5 100644 --- a/src/calibre/gui2/wizard/__init__.py +++ b/src/calibre/gui2/wizard/__init__.py @@ -623,8 +623,8 @@ class DevicePage(QWizardPage, DeviceUI): def __init__(self): QWizardPage.__init__(self) self.setupUi(self) - self.registerField("manufacturer", self.manufacturer_view) - self.registerField("device", self.device_view) + self.registerField('manufacturer', self.manufacturer_view) + self.registerField('device', self.device_view) def initializePage(self): self.label.setText(_('Choose your e-book device. If your device is' @@ -950,7 +950,7 @@ class Wizard(QWizard): QWizard.accept(self) def set_finish_text(self, *args): - bt = str("<em>" + self.buttonText(QWizard.WizardButton.FinishButton) + "</em>").replace('&', '') + bt = str('<em>' + self.buttonText(QWizard.WizardButton.FinishButton) + '</em>').replace('&', '') t = str(self.finish_page.finish_text.text()) if '%s' in t: self.finish_page.finish_text.setText(t%bt) diff --git a/src/calibre/gui2/wizard/send_email.py b/src/calibre/gui2/wizard/send_email.py index dfbf62e65a..7252944593 100644 --- a/src/calibre/gui2/wizard/send_email.py +++ b/src/calibre/gui2/wizard/send_email.py @@ -42,11 +42,11 @@ class TestEmail(QDialog): def __init__(self, pa, parent): QDialog.__init__(self, parent) self.test_func = parent.test_email_settings - self.setWindowTitle(_("Test email settings")) + self.setWindowTitle(_('Test email settings')) self.setWindowIcon(QIcon.ic('config.ui')) l = QVBoxLayout(self) opts = smtp_prefs().parse() - self.from_ = la = QLabel(_("Send test mail from %s to:")%opts.from_) + self.from_ = la = QLabel(_('Send test mail from %s to:')%opts.from_) l.addWidget(la) self.to = le = QLineEdit(self) if pa: @@ -111,7 +111,7 @@ class RelaySetup(QDialog): bb.rejected.connect(self.reject) self.tl = QLabel(('<p>'+_('Setup sending email using') + ' <b>{name}</b><p>' + - _('If you don\'t have an account, you can sign up for a free {name} email ' + _("If you don't have an account, you can sign up for a free {name} email " 'account at <a href="https://{url}">{url}</a>. {extra}')).format( **service)) l.addWidget(self.tl, 0, 0, 3, 0) diff --git a/src/calibre/library/catalogs/bibtex.py b/src/calibre/library/catalogs/bibtex.py index ac2397c290..3baef07004 100644 --- a/src/calibre/library/catalogs/bibtex.py +++ b/src/calibre/library/catalogs/bibtex.py @@ -127,9 +127,9 @@ class BIBTEX(CatalogPlugin): # Define starting chain or if book valid strict and not book return a Fail string bibtex_entry = [] - if mode != "misc" and check_entry_book_valid(entry) : + if mode != 'misc' and check_entry_book_valid(entry) : bibtex_entry.append('@book{') - elif mode != "book" : + elif mode != 'book' : bibtex_entry.append('@misc{') else : # case strict book @@ -191,7 +191,7 @@ class BIBTEX(CatalogPlugin): try: item = html2text(item) except: - log.warn("Failed to convert comments to text") + log.warn('Failed to convert comments to text') bibtex_entry.append('note = "%s"' % bibtexdict.utf8ToBibtex(item)) elif field == 'isbn' : @@ -215,7 +215,7 @@ class BIBTEX(CatalogPlugin): elif field == 'pubdate' : bibtex_entry.append('year = "%s"' % item.year) - bibtex_entry.append('month = "%s"' % bibtexdict.utf8ToBibtex(strftime("%b", item))) + bibtex_entry.append('month = "%s"' % bibtexdict.utf8ToBibtex(strftime('%b', item))) elif field.startswith('#') and isinstance(item, string_or_bytes): bibtex_entry.append('custom_{} = "{}"'.format(field[1:], @@ -269,11 +269,11 @@ class BIBTEX(CatalogPlugin): if len(tpl_citation) >0 : return tpl_citation - if len(entry["isbn"]) > 0 : - template_citation = '%s' % re.sub(r'[\D]','', entry["isbn"]) + if len(entry['isbn']) > 0 : + template_citation = '%s' % re.sub(r'[\D]','', entry['isbn']) else : - template_citation = '%s' % str(entry["id"]) + template_citation = '%s' % str(entry['id']) return bibtexclass.ValidateCitationKey(template_citation) @@ -294,39 +294,39 @@ class BIBTEX(CatalogPlugin): if opts.bibfile_enc in bibfile_enc : bibfile_enc = opts.bibfile_enc else : - log.warn("Incorrect --choose-encoding flag, revert to default") + log.warn('Incorrect --choose-encoding flag, revert to default') bibfile_enc = bibfile_enc[0] if opts.bibfile_enctag in bibfile_enctag : bibfile_enctag = opts.bibfile_enctag else : - log.warn("Incorrect --choose-encoding-configuration flag, revert to default") + log.warn('Incorrect --choose-encoding-configuration flag, revert to default') bibfile_enctag = bibfile_enctag[0] if opts.bib_entry in bib_entry : bib_entry = opts.bib_entry else : - log.warn("Incorrect --entry-type flag, revert to default") + log.warn('Incorrect --entry-type flag, revert to default') bib_entry = bib_entry[0] if opts.verbose: opts_dict = vars(opts) - log(f"{self.name}(): Generating {self.fmt}") + log(f'{self.name}(): Generating {self.fmt}') if opts.connected_device['is_device_connected']: - log(" connected_device: %s" % opts.connected_device['name']) + log(' connected_device: %s' % opts.connected_device['name']) if opts_dict['search_text']: log(" --search='%s'" % opts_dict['search_text']) if opts_dict['ids']: - log(" Book count: %d" % len(opts_dict['ids'])) + log(' Book count: %d' % len(opts_dict['ids'])) if opts_dict['search_text']: - log(" (--search ignored when a subset of the database is specified)") + log(' (--search ignored when a subset of the database is specified)') if opts_dict['fields']: if opts_dict['fields'] == 'all': - log(" Fields: %s" % ', '.join(FIELDS[1:])) + log(' Fields: %s' % ', '.join(FIELDS[1:])) else: - log(" Fields: %s" % opts_dict['fields']) + log(' Fields: %s' % opts_dict['fields']) - log(f" Output file will be encoded in {bibfile_enc} with {bibfile_enctag} flag") + log(f' Output file will be encoded in {bibfile_enc} with {bibfile_enctag} flag') log(" BibTeX entry type is {} with a citation like '{}' flag".format(bib_entry, opts_dict['bib_cit'])) @@ -361,7 +361,7 @@ class BIBTEX(CatalogPlugin): elif opts.impcit == 'True' : citation_bibtex= True else : - log.warn("Incorrect --create-citation, revert to default") + log.warn('Incorrect --create-citation, revert to default') citation_bibtex= True else : citation_bibtex= opts.impcit @@ -373,7 +373,7 @@ class BIBTEX(CatalogPlugin): elif opts.addfiles == 'True' : addfiles_bibtex = True else : - log.warn("Incorrect --add-files-path, revert to default") + log.warn('Incorrect --add-files-path, revert to default') addfiles_bibtex= True else : addfiles_bibtex = opts.addfiles @@ -391,7 +391,7 @@ class BIBTEX(CatalogPlugin): if bib_entry == 'book' : nb_books = len(list(filter(check_entry_book_valid, data))) if nb_books < nb_entries : - log.warn("Only %d entries in %d are book compatible" % (nb_books, nb_entries)) + log.warn('Only %d entries in %d are book compatible' % (nb_books, nb_entries)) nb_entries = nb_books # If connected device, add 'On Device' values to data @@ -401,7 +401,7 @@ class BIBTEX(CatalogPlugin): # outfile.write('%%%Calibre catalog\n%%%{0} entries in catalog\n\n'.format(nb_entries)) outfile.write('@preamble{"This catalog of %d entries was generated by calibre on %s"}\n\n' - % (nb_entries, strftime("%A, %d. %B %Y %H:%M"))) + % (nb_entries, strftime('%A, %d. %B %Y %H:%M'))) for entry in data: outfile.write(create_bibtex_entry(entry, fields, bib_entry, template_citation, diff --git a/src/calibre/library/catalogs/csv_xml.py b/src/calibre/library/catalogs/csv_xml.py index dae26bf0cd..2f629a594e 100644 --- a/src/calibre/library/catalogs/csv_xml.py +++ b/src/calibre/library/catalogs/csv_xml.py @@ -71,20 +71,20 @@ class CSV_XML(CatalogPlugin): opts_dict = vars(opts) log(f"{self.name}('{current_library}'): Generating {self.fmt.upper()}") if opts.connected_device['is_device_connected']: - log(" connected_device: %s" % opts.connected_device['name']) + log(' connected_device: %s' % opts.connected_device['name']) if opts_dict['search_text']: log(" --search='%s'" % opts_dict['search_text']) if opts_dict['ids']: - log(" Book count: %d" % len(opts_dict['ids'])) + log(' Book count: %d' % len(opts_dict['ids'])) if opts_dict['search_text']: - log(" (--search ignored when a subset of the database is specified)") + log(' (--search ignored when a subset of the database is specified)') if opts_dict['fields']: if opts_dict['fields'] == 'all': - log(" Fields: %s" % ', '.join(FIELDS[1:])) + log(' Fields: %s' % ', '.join(FIELDS[1:])) else: - log(" Fields: %s" % opts_dict['fields']) + log(' Fields: %s' % opts_dict['fields']) # If a list of ids are provided, don't use search_text if opts.ids: diff --git a/src/calibre/library/catalogs/epub_mobi.py b/src/calibre/library/catalogs/epub_mobi.py index df9b6a7f09..00c73ed14e 100644 --- a/src/calibre/library/catalogs/epub_mobi.py +++ b/src/calibre/library/catalogs/epub_mobi.py @@ -33,8 +33,8 @@ class EPUB_MOBI(CatalogPlugin): version = (1, 0, 0) file_types = {'azw3', 'epub', 'mobi'} - THUMB_SMALLEST = "1.0" - THUMB_LARGEST = "3.0" + THUMB_SMALLEST = '1.0' + THUMB_LARGEST = '3.0' cli_options = [Option('--catalog-title', # {{{ default='My Books', @@ -78,8 +78,8 @@ class EPUB_MOBI(CatalogPlugin): "(('Archived books','#status','Archived'),)\n" "will exclude a book with a value of 'Archived' in the custom column 'status'.\n" "When multiple rules are defined, all rules will be applied.\n" - "Default: \n" + '"' + '%default' + '"' + "\n" - "Applies to: AZW3, EPUB, MOBI output formats")), + "Default: \n" + '"' + '%default' + '"' + '\n' + 'Applies to: AZW3, EPUB, MOBI output formats')), Option('--generate-authors', default=False, dest='generate_authors', @@ -162,8 +162,8 @@ class EPUB_MOBI(CatalogPlugin): help=_("Specifies the rules used to include prefixes indicating read books, wishlist items and other user-specified prefixes.\n" "The model for a prefix rule is ('<rule name>','<source field>','<pattern>','<prefix>').\n" "When multiple rules are defined, the first matching rule will be used.\n" - "Default:\n" + '"' + '%default' + '"' + "\n" - "Applies to: AZW3, EPUB, MOBI output formats")), + "Default:\n" + '"' + '%default' + '"' + '\n' + 'Applies to: AZW3, EPUB, MOBI output formats')), Option('--preset', default=None, dest='preset', @@ -197,7 +197,7 @@ class EPUB_MOBI(CatalogPlugin): # If preset specified from the cli, insert stored options from JSON file if hasattr(opts, 'preset') and opts.preset: - available_presets = JSONConfig("catalog_presets") + available_presets = JSONConfig('catalog_presets') if opts.preset not in available_presets: if available_presets: print(_('Error: Preset "{}" not found.').format(opts.preset)) @@ -249,15 +249,15 @@ class EPUB_MOBI(CatalogPlugin): opts.connected_kindle = True if opts.connected_device['serial'] and \ opts.connected_device['serial'][:4] in ['B004', 'B005']: - op = "kindle_dx" + op = 'kindle_dx' else: - op = "kindle" + op = 'kindle' opts.description_clip = 380 if op.endswith('dx') or 'kindle' not in op else 100 opts.author_clip = 100 if op.endswith('dx') or 'kindle' not in op else 60 opts.output_profile = op - opts.basename = "Catalog" + opts.basename = 'Catalog' opts.cli_environment = not hasattr(opts, 'sync') # Hard-wired to always sort descriptions by author, with series after non-series @@ -289,21 +289,21 @@ class EPUB_MOBI(CatalogPlugin): 'x' * (len(opts.connected_device['serial']) - 4))) for storage in opts.connected_device['storage']: if storage: - build_log.append(" mount point: %s" % storage) + build_log.append(' mount point: %s' % storage) else: build_log.append(" connected_device: '%s'" % opts.connected_device['name']) try: for storage in opts.connected_device['storage']: if storage: - build_log.append(" mount point: %s" % storage) + build_log.append(' mount point: %s' % storage) except: - build_log.append(" (no mount points)") + build_log.append(' (no mount points)') else: build_log.append(" connected_device: '%s'" % opts.connected_device['name']) opts_dict = vars(opts) if opts_dict['ids']: - build_log.append(" book count: %d" % len(opts_dict['ids'])) + build_log.append(' book count: %d' % len(opts_dict['ids'])) sections_list = [] if opts.generate_authors: @@ -331,14 +331,14 @@ class EPUB_MOBI(CatalogPlugin): sections_list = ['Authors', 'Titles', 'Series', 'Genres', 'Recently Added', 'Descriptions'] else: opts.log.warn('\n*** No enabled Sections, terminating catalog generation ***') - return ["No Included Sections", "No enabled Sections.\nCheck E-book options tab\n'Included sections'\n"] + return ['No Included Sections', "No enabled Sections.\nCheck E-book options tab\n'Included sections'\n"] if opts.fmt == 'mobi' and sections_list == ['Descriptions']: warning = _("\n*** Adding 'By authors' section required for MOBI output ***") opts.log.warn(warning) sections_list.insert(0, 'Authors') opts.generate_authors = True - opts.log(" Sections: %s" % ', '.join(sections_list)) + opts.log(' Sections: %s' % ', '.join(sections_list)) opts.section_list = sections_list # Limit thumb_width to 1.0" - 2.0" @@ -349,36 +349,36 @@ class EPUB_MOBI(CatalogPlugin): if float(opts.thumb_width) > float(self.THUMB_LARGEST): log.warning(f"coercing thumb_width from '{opts.thumb_width}' to '{self.THUMB_LARGEST}'") opts.thumb_width = self.THUMB_LARGEST - opts.thumb_width = "%.2f" % float(opts.thumb_width) + opts.thumb_width = '%.2f' % float(opts.thumb_width) except Exception: log.error(f"coercing thumb_width from '{opts.thumb_width}' to '{self.THUMB_SMALLEST}'") - opts.thumb_width = "1.0" + opts.thumb_width = '1.0' # eval prefix_rules if passed from command line if type(opts.prefix_rules) is not tuple: try: opts.prefix_rules = eval(opts.prefix_rules) except: - log.error("malformed --prefix-rules: %s" % opts.prefix_rules) + log.error('malformed --prefix-rules: %s' % opts.prefix_rules) raise for rule in opts.prefix_rules: if len(rule) != 4: - log.error("incorrect number of args for --prefix-rules: %s" % repr(rule)) + log.error('incorrect number of args for --prefix-rules: %s' % repr(rule)) # eval exclusion_rules if passed from command line if type(opts.exclusion_rules) is not tuple: try: opts.exclusion_rules = eval(opts.exclusion_rules) except: - log.error("malformed --exclusion-rules: %s" % opts.exclusion_rules) + log.error('malformed --exclusion-rules: %s' % opts.exclusion_rules) raise for rule in opts.exclusion_rules: if len(rule) != 3: - log.error("incorrect number of args for --exclusion-rules: %s" % repr(rule)) + log.error('incorrect number of args for --exclusion-rules: %s' % repr(rule)) # Display opts keys = sorted(opts_dict.keys()) - build_log.append(" opts:") + build_log.append(' opts:') for key in keys: if key in ['catalog_title', 'author_clip', 'connected_kindle', 'creator', 'cross_reference_authors', 'description_clip', 'exclude_book_marker', @@ -387,7 +387,7 @@ class EPUB_MOBI(CatalogPlugin): 'output_profile', 'prefix_rules', 'preset', 'read_book_marker', 'search_text', 'sort_by', 'sort_descriptions_by_author', 'sync', 'thumb_width', 'use_existing_cover', 'wishlist_tag']: - build_log.append(f" {key}: {repr(opts_dict[key])}") + build_log.append(f' {key}: {repr(opts_dict[key])}') if opts.verbose: log('\n'.join(line for line in build_log)) @@ -397,7 +397,7 @@ class EPUB_MOBI(CatalogPlugin): self.opts = opts if opts.verbose: - log.info(" Begin catalog source generation (%s)" % + log.info(' Begin catalog source generation (%s)' % str(datetime.timedelta(seconds=int(time.time() - opts.start_time)))) # Launch the Catalog builder @@ -406,12 +406,12 @@ class EPUB_MOBI(CatalogPlugin): try: catalog.build_sources() if opts.verbose: - log.info(" Completed catalog source generation (%s)\n" % + log.info(' Completed catalog source generation (%s)\n' % str(datetime.timedelta(seconds=int(time.time() - opts.start_time)))) except (AuthorSortMismatchException, EmptyCatalogException) as e: - log.error(" *** Terminated catalog generation: %s ***" % e) + log.error(' *** Terminated catalog generation: %s ***' % e) except: - log.error(" unhandled exception in catalog generator") + log.error(' unhandled exception in catalog generator') raise else: @@ -420,9 +420,9 @@ class EPUB_MOBI(CatalogPlugin): OptionRecommendation.HIGH)) recommendations.append(('comments', '', OptionRecommendation.HIGH)) - """ + ''' >>> Use to debug generated catalog code before pipeline conversion <<< - """ + ''' GENERATE_DEBUG_EPUB = False if GENERATE_DEBUG_EPUB: catalog_debug_path = os.path.join(os.path.expanduser('~'), 'Desktop', 'Catalog debug') @@ -433,7 +433,7 @@ class EPUB_MOBI(CatalogPlugin): recommendations.append(('debug_pipeline', dp, OptionRecommendation.HIGH)) - if opts.output_profile and opts.output_profile.startswith("kindle"): + if opts.output_profile and opts.output_profile.startswith('kindle'): recommendations.append(('output_profile', opts.output_profile, OptionRecommendation.HIGH)) recommendations.append(('book_producer', opts.output_profile, @@ -459,14 +459,14 @@ class EPUB_MOBI(CatalogPlugin): pass if self.opts.use_existing_cover and not existing_cover: - log.warning("no existing catalog cover found") + log.warning('no existing catalog cover found') if self.opts.use_existing_cover and existing_cover: recommendations.append(('cover', cpath, OptionRecommendation.HIGH)) - log.info("using existing catalog cover") + log.info('using existing catalog cover') else: from calibre.ebooks.covers import calibre_cover2 - log.info("replacing catalog cover") + log.info('replacing catalog cover') new_cover_path = PersistentTemporaryFile(suffix='.jpg') new_cover = calibre_cover2(opts.catalog_title, 'calibre') new_cover_path.write(new_cover) @@ -499,7 +499,7 @@ class EPUB_MOBI(CatalogPlugin): zip_rebuilder(input_path, os.path.join(catalog_debug_path, 'input.epub')) if opts.verbose: - log.info(" Catalog creation complete (%s)\n" % + log.info(' Catalog creation complete (%s)\n' % str(datetime.timedelta(seconds=int(time.time() - opts.start_time)))) # returns to gui2.actions.catalog:catalog_generated() diff --git a/src/calibre/library/catalogs/epub_mobi_builder.py b/src/calibre/library/catalogs/epub_mobi_builder.py index 4abb6c026d..7be0bedd60 100644 --- a/src/calibre/library/catalogs/epub_mobi_builder.py +++ b/src/calibre/library/catalogs/epub_mobi_builder.py @@ -138,7 +138,7 @@ class CatalogBuilder: def __init__(self, db, _opts, plugin, report_progress=DummyReporter(), - stylesheet="content/stylesheet.css", + stylesheet='content/stylesheet.css', init_resources=True): self.formatter = Formatter() @@ -148,15 +148,15 @@ class CatalogBuilder: self.reporter = report_progress self.stylesheet = stylesheet self.cache_dir = os.path.join(cache_dir(), 'catalog') - self.catalog_path = PersistentTemporaryDirectory("_epub_mobi_catalog", prefix='') - self.content_dir = os.path.join(self.catalog_path, "content") + self.catalog_path = PersistentTemporaryDirectory('_epub_mobi_catalog', prefix='') + self.content_dir = os.path.join(self.catalog_path, 'content') self.excluded_tags = self.get_excluded_tags() self.generate_for_kindle_azw3 = True if (_opts.fmt == 'azw3' and _opts.output_profile and - _opts.output_profile.startswith("kindle")) else False + _opts.output_profile.startswith('kindle')) else False self.generate_for_kindle_mobi = True if (_opts.fmt == 'mobi' and _opts.output_profile and - _opts.output_profile.startswith("kindle")) else False + _opts.output_profile.startswith('kindle')) else False self.all_series = set() self.authors = None @@ -175,7 +175,7 @@ class CatalogBuilder: self.generate_recently_read = False self.genres = [] self.genre_tags_dict = \ - self.filter_genre_tags(max_len=245 - len("%s/Genre_.html" % self.content_dir)) \ + self.filter_genre_tags(max_len=245 - len('%s/Genre_.html' % self.content_dir)) \ if self.opts.generate_genres else None self.html_filelist_1 = [] self.html_filelist_2 = [] @@ -191,7 +191,7 @@ class CatalogBuilder: self.thumb_height = 0 self.thumb_width = 0 self.thumbs = None - self.thumbs_path = os.path.join(self.cache_dir, "thumbs.zip") + self.thumbs_path = os.path.join(self.cache_dir, 'thumbs.zip') self.total_steps = 6.0 self.use_series_prefix_in_titles_section = False @@ -204,7 +204,7 @@ class CatalogBuilder: if init_resources: self.copy_catalog_resources() - """ key() functions """ + ''' key() functions ''' def _kf_author_to_author_sort(self, author): """ Compute author_sort value from author @@ -252,7 +252,7 @@ class CatalogBuilder: return key def _kf_books_by_author_sorter_author_sort(self, book, longest_author_sort=60): - """ Generate book sort key with supplied author_sort. + ''' Generate book sort key with supplied author_sort. Generate a sort key of author_sort, title. Bang, tilde included to force series to sort after non-series books. @@ -262,7 +262,7 @@ class CatalogBuilder: Return: (str): sort key - """ + ''' if not book['series']: fs = '{:<%d}!{!s}' % longest_author_sort key = fs.format(capitalize(book['author_sort']), @@ -287,10 +287,10 @@ class CatalogBuilder: series_index) return key - """ Methods """ + ''' Methods ''' def build_sources(self): - """ Generate catalog source files. + ''' Generate catalog source files. Assemble OPF, HTML and NCX files reflecting catalog options. Generated source is OEB compliant. @@ -305,7 +305,7 @@ class CatalogBuilder: Results: error: problems reported during build - """ + ''' self.fetch_books_by_title() self.fetch_books_by_author() @@ -323,13 +323,13 @@ class CatalogBuilder: self.generate_html_by_genres() # If this is the only Section, and there are no genres, bail if self.opts.section_list == ['Genres'] and not self.genres: - error_msg = _("No genres to catalog.\n") + error_msg = _('No genres to catalog.\n') if not self.opts.cli_environment: error_msg += _("Check 'Excluded genres' regex in the E-book options.\n") self.opts.log.error(error_msg) self.error.append(_('No books available to catalog')) self.error.append(error_msg) - raise EmptyCatalogException("No genres to catalog") + raise EmptyCatalogException('No genres to catalog') if self.opts.generate_recently_added: self.generate_html_by_date_added() if self.generate_recently_read: @@ -338,23 +338,23 @@ class CatalogBuilder: self.generate_opf() self.generate_ncx_header() if self.opts.generate_authors: - self.generate_ncx_by_author(_("Authors")) + self.generate_ncx_by_author(_('Authors')) if self.opts.generate_titles: - self.generate_ncx_by_title(_("Titles")) + self.generate_ncx_by_title(_('Titles')) if self.opts.generate_series: self.generate_ncx_by_series(ngettext('Series', 'Series', 2)) if self.opts.generate_genres: - self.generate_ncx_by_genre(_("Genres")) + self.generate_ncx_by_genre(_('Genres')) if self.opts.generate_recently_added: - self.generate_ncx_by_date_added(_("Recently Added")) + self.generate_ncx_by_date_added(_('Recently Added')) if self.generate_recently_read: - self.generate_ncx_by_date_read(_("Recently Read")) + self.generate_ncx_by_date_read(_('Recently Read')) if self.opts.generate_descriptions: - self.generate_ncx_descriptions(_("Descriptions")) + self.generate_ncx_descriptions(_('Descriptions')) self.write_ncx() def calculate_thumbnail_dimensions(self): - """ Calculate thumb dimensions based on device DPI. + ''' Calculate thumb dimensions based on device DPI. Using the specified output profile, calculate thumb_width in pixels, then set height to width * 1.33. Special-case for @@ -368,7 +368,7 @@ class CatalogBuilder: Outputs: thumb_width (float): calculated thumb_width thumb_height (float): calculated thumb_height - """ + ''' for x in output_profiles(): if x.short_name == self.opts.output_profile: @@ -381,12 +381,12 @@ class CatalogBuilder: self.thumb_height = self.thumb_height // 2 break if self.opts.verbose: - self.opts.log(" Thumbnails:") - self.opts.log(" DPI = %d; thumbnail dimensions: %d x %d" % + self.opts.log(' Thumbnails:') + self.opts.log(' DPI = %d; thumbnail dimensions: %d x %d' % (x.dpi, self.thumb_width, self.thumb_height)) def compute_total_steps(self): - """ Calculate number of build steps to generate catalog. + ''' Calculate number of build steps to generate catalog. Calculate total number of build steps based on enabled sections. @@ -395,7 +395,7 @@ class CatalogBuilder: Outputs: total_steps (int): updated - """ + ''' # Tweak build steps based on optional sections: 1 call for HTML, 1 for NCX incremental_jobs = 0 if self.opts.generate_authors: @@ -414,7 +414,7 @@ class CatalogBuilder: self.total_steps += incremental_jobs def confirm_thumbs_archive(self): - """ Validate thumbs archive. + ''' Validate thumbs archive. Confirm existence of thumbs archive, or create if absent. Confirm stored thumb_width matches current opts.thumb_width, @@ -428,7 +428,7 @@ class CatalogBuilder: Outputs: thumbs_path (file): new (non_existent or invalidated), or validated existing thumbs archive - """ + ''' if self.opts.generate_descriptions: if not os.path.exists(self.cache_dir): self.opts.log.info(" creating new thumb cache '%s'" % self.cache_dir) @@ -437,14 +437,14 @@ class CatalogBuilder: self.opts.log.info(' creating thumbnail archive, thumb_width: %1.2f"' % float(self.opts.thumb_width)) with ZipFile(self.thumbs_path, mode='w') as zfw: - zfw.writestr("Catalog Thumbs Archive", '') + zfw.writestr('Catalog Thumbs Archive', '') else: try: with ZipFile(self.thumbs_path, mode='r') as zfr: try: cached_thumb_width = zfr.read('thumb_width') except: - cached_thumb_width = "-1" + cached_thumb_width = '-1' except: os.remove(self.thumbs_path) cached_thumb_width = '-1' @@ -454,7 +454,7 @@ class CatalogBuilder: self.opts.log.warning(' thumb_width changed: %1.2f" => %1.2f"' % (float(cached_thumb_width), float(self.opts.thumb_width))) with ZipFile(self.thumbs_path, mode='w') as zfw: - zfw.writestr("Catalog Thumbs Archive", '') + zfw.writestr('Catalog Thumbs Archive', '') else: self.opts.log.info(' existing thumb cache at %s, cached_thumb_width: %1.2f"' % (self.thumbs_path, float(cached_thumb_width))) @@ -474,7 +474,7 @@ class CatalogBuilder: return replace_entities(s) def copy_catalog_resources(self): - """ Copy resources from calibre source to self.catalog_path. + ''' Copy resources from calibre source to self.catalog_path. Copy basic resources - default cover, stylesheet, and masthead (Kindle only) from calibre resource directory to self.catalog_path, a temporary directory @@ -485,9 +485,9 @@ class CatalogBuilder: Output: resource files copied to self.catalog_path/* - """ + ''' self.create_catalog_directory_structure() - catalog_resources = P("catalog") + catalog_resources = P('catalog') files_to_copy = [('', 'DefaultCover.jpg'), ('content', 'stylesheet.css')] @@ -510,7 +510,7 @@ class CatalogBuilder: pass def create_catalog_directory_structure(self): - """ Create subdirs in catalog output dir. + ''' Create subdirs in catalog output dir. Create /content and /images in self.catalog_path @@ -519,19 +519,19 @@ class CatalogBuilder: Output: /content, /images created - """ + ''' if not os.path.isdir(self.catalog_path): os.makedirs(self.catalog_path) - content_path = self.catalog_path + "/content" + content_path = self.catalog_path + '/content' if not os.path.isdir(content_path): os.makedirs(content_path) - images_path = self.catalog_path + "/images" + images_path = self.catalog_path + '/images' if not os.path.isdir(images_path): os.makedirs(images_path) def detect_author_sort_mismatches(self, books_to_test): - """ Detect author_sort mismatches. + ''' Detect author_sort mismatches. Sort by author, look for inconsistencies in author_sort among similarly-named authors. Fatal for MOBI generation, a mere @@ -545,7 +545,7 @@ class CatalogBuilder: Exceptions: AuthorSortMismatchException: author_sort mismatch detected - """ + ''' books_by_author = sorted(list(books_to_test), key=self._kf_books_by_author_sorter_author) @@ -556,10 +556,10 @@ class CatalogBuilder: if author[0] == current_author[0]: if self.opts.fmt == 'mobi': # Exit if building MOBI - error_msg = _("<p>Inconsistent author sort values for author<br/>" + + error_msg = _('<p>Inconsistent author sort values for author<br/>' + f"'{author[0]!s}':</p>" + - f"<p><center><b>{author[1]!s}</b> != <b>{current_author[1]!s}</b></center></p>" + - "<p>Unable to build MOBI catalog.<br/>" + + f'<p><center><b>{author[1]!s}</b> != <b>{current_author[1]!s}</b></center></p>' + + '<p>Unable to build MOBI catalog.<br/>' + f"Select all books by '{author[0]!s}', apply correct Author Sort value in Edit Metadata dialog, then rebuild the catalog.\n<p>") # noqa: E501 self.opts.log.warn('\n*** Metadata error ***') @@ -567,14 +567,14 @@ class CatalogBuilder: self.error.append('Author sort mismatch') self.error.append(error_msg) - raise AuthorSortMismatchException("author_sort mismatch while building MOBI") + raise AuthorSortMismatchException('author_sort mismatch while building MOBI') else: # Warning if building non-MOBI if not self.error: self.error.append('Author sort mismatch') error_msg = _(f"Warning: Inconsistent author sort values for author '{author[0]!s}':\n" + - f" {author[1]!s} != {current_author[1]!s}\n") + f' {author[1]!s} != {current_author[1]!s}\n') self.opts.log.warn('\n*** Metadata warning ***') self.opts.log.warn(error_msg) self.error.append(error_msg) @@ -583,7 +583,7 @@ class CatalogBuilder: current_author = author def discover_prefix(self, record): - """ Return a prefix for record. + ''' Return a prefix for record. Evaluate record against self.prefix_rules. Return assigned prefix if matched. @@ -594,7 +594,7 @@ class CatalogBuilder: Return: prefix (str): matched a prefix_rule None: no match - """ + ''' def _log_prefix_rule_match_info(rule, record, matched): self.opts.log.info(" %s '%s' by %s (%s: '%s' contains '%s')" % (rule['prefix'], record['title'], @@ -643,7 +643,7 @@ class CatalogBuilder: return rule['prefix'] except: if self.opts.verbose: - self.opts.log.error("pattern failed to compile: %s" % rule['pattern']) + self.opts.log.error('pattern failed to compile: %s' % rule['pattern']) pass elif field_contents is None and rule['pattern'] == 'None': if self.DEBUG: @@ -653,19 +653,19 @@ class CatalogBuilder: return None def dump_custom_fields(self): - """ + ''' Dump custom field mappings for debugging - """ + ''' if self.opts.verbose: - self.opts.log.info(" Custom fields:") + self.opts.log.info(' Custom fields:') all_custom_fields = self.db.custom_field_keys() for cf in all_custom_fields: - self.opts.log.info(" %-20s %-20s %s" % + self.opts.log.info(' %-20s %-20s %s' % (cf, "'%s'" % self.db.metadata_for_field(cf)['name'], self.db.metadata_for_field(cf)['datatype'])) def establish_equivalencies(self, item_list, key=None): - """ Return icu equivalent sort letter. + ''' Return icu equivalent sort letter. Returns base sort letter for accented characters. Code provided by chaley, modified to force unaccented base letters for A, O & U when @@ -676,7 +676,7 @@ class CatalogBuilder: Return: cl_list (list): list of equivalent leading chars, 1:1 correspondence to item_list - """ + ''' # Hack to force the cataloged leading letter to be # an unadorned character if the accented version sorts before the unaccented @@ -732,17 +732,17 @@ class CatalogBuilder: cl_list[idx] = last_c if self.DEBUG and self.opts.verbose: - print(" establish_equivalencies():") + print(' establish_equivalencies():') if key: for idx, item in enumerate(item_list): - print(f" {cl_list[idx]} {item[sort_field]}") + print(f' {cl_list[idx]} {item[sort_field]}') else: - print(f" {cl_list[idx]} {item}") + print(f' {cl_list[idx]} {item}') return cl_list def fetch_books_by_author(self): - """ Generate a list of books sorted by author. + ''' Generate a list of books sorted by author. For books with multiple authors, relist book with additional authors. Sort the database by author. Report author_sort inconsistencies as warning when @@ -761,9 +761,9 @@ class CatalogBuilder: Return: True: no errors False: author_sort mismatch detected while building MOBI - """ + ''' - self.update_progress_full_step(_("Sorting database")) + self.update_progress_full_step(_('Sorting database')) books_by_author = list(self.books_to_catalog) self.detect_author_sort_mismatches(books_by_author) @@ -840,18 +840,18 @@ class CatalogBuilder: self.individual_authors = list(individual_authors) if self.DEBUG and self.opts.verbose: - self.opts.log.info("\nfetch_books_by_author(): %d unique authors" % len(unique_authors)) + self.opts.log.info('\nfetch_books_by_author(): %d unique authors' % len(unique_authors)) for author in unique_authors: - self.opts.log.info((" %-50s %-25s %2d" % (author[0][0:45], author[1][0:20], + self.opts.log.info((' %-50s %-25s %2d' % (author[0][0:45], author[1][0:20], author[2])).encode('utf-8')) - self.opts.log.info("\nfetch_books_by_author(): %d individual authors" % len(individual_authors)) + self.opts.log.info('\nfetch_books_by_author(): %d individual authors' % len(individual_authors)) for author in sorted(individual_authors): - self.opts.log.info("%s" % author) + self.opts.log.info('%s' % author) return True def fetch_books_by_title(self): - """ Generate a list of books sorted by title. + ''' Generate a list of books sorted by title. Sort the database by title. @@ -864,17 +864,17 @@ class CatalogBuilder: Return: True: no errors False: author_sort mismatch detected while building MOBI - """ - self.update_progress_full_step(_("Sorting titles")) + ''' + self.update_progress_full_step(_('Sorting titles')) # Re-sort based on title_sort if len(self.books_to_catalog): self.books_by_title = sorted(self.books_to_catalog, key=lambda x: sort_key(x['title_sort'].upper())) if self.DEBUG and self.opts.verbose: - self.opts.log.info("fetch_books_by_title(): %d books" % len(self.books_by_title)) - self.opts.log.info(" %-40s %-40s" % ('title', 'title_sort')) + self.opts.log.info('fetch_books_by_title(): %d books' % len(self.books_by_title)) + self.opts.log.info(' %-40s %-40s' % ('title', 'title_sort')) for title in self.books_by_title: - self.opts.log.info((" %-40s %-40s" % (title['title'][0:40], + self.opts.log.info((' %-40s %-40s' % (title['title'][0:40], title['title_sort'][0:40])).encode('utf-8')) else: error_msg = _("No books to catalog.\nCheck 'Excluded books' rules in the E-book options.\n") @@ -941,7 +941,7 @@ class CatalogBuilder: this_title['authors'] = record['authors'] # Synthesize author attribution from authors list if record['authors']: - this_title['author'] = " & ".join(record['authors']) + this_title['author'] = ' & '.join(record['authors']) else: this_title['author'] = _('Unknown') this_title['authors'] = [this_title['author']] @@ -981,7 +981,7 @@ class CatalogBuilder: for token in p.contents: if token.string is not None: tokens.append(token.string) - this_title['short_description'] = self.generate_short_description(' '.join(tokens), dest="description") + this_title['short_description'] = self.generate_short_description(' '.join(tokens), dest='description') else: this_title['description'] = None this_title['short_description'] = None @@ -1046,14 +1046,14 @@ class CatalogBuilder: search_terms = [] for tag in self.excluded_tags: search_terms.append('tags:"=%s"' % tag) - search_phrase = "not (%s)" % " or ".join(search_terms) + search_phrase = 'not (%s)' % ' or '.join(search_terms) # If a list of ids are provided, don't use search_text if self.opts.ids: self.opts.search_text = search_phrase else: if self.opts.search_text: - self.opts.search_text += " " + search_phrase + self.opts.search_text += ' ' + search_phrase else: self.opts.search_text = search_phrase @@ -1063,9 +1063,9 @@ class CatalogBuilder: if self.DEBUG: if self.prefix_rules: - self.opts.log.info(" Added prefixes (bools_are_tristate: {}):".format(self.db.new_api.pref('bools_are_tristate'))) + self.opts.log.info(' Added prefixes (bools_are_tristate: {}):'.format(self.db.new_api.pref('bools_are_tristate'))) else: - self.opts.log.info(" No added prefixes") + self.opts.log.info(' No added prefixes') # Populate this_title{} from data[{},{}] titles = [] @@ -1075,7 +1075,7 @@ class CatalogBuilder: return titles def fetch_bookmarks(self): - """ Interrogate connected Kindle for bookmarks. + ''' Interrogate connected Kindle for bookmarks. Discover bookmarks associated with books on Kindle downloaded by calibre. Used in Descriptions to show reading progress, Last Read section showing date @@ -1089,7 +1089,7 @@ class CatalogBuilder: Output: bookmarked_books (dict): dict of Bookmarks - """ + ''' from calibre.devices.kindle.bookmark import Bookmark from calibre.devices.usbms.device import Device @@ -1152,7 +1152,7 @@ class CatalogBuilder: self.bookmarked_books = {} if self.generate_recently_read: - self.opts.log.info(" Collecting Kindle bookmarks matching catalog entries") + self.opts.log.info(' Collecting Kindle bookmarks matching catalog entries') d = BookmarkDevice(None) d.initialize(self.opts.connected_device['save_template']) @@ -1187,7 +1187,7 @@ class CatalogBuilder: self.bookmarked_books = bookmarks def filter_genre_tags(self, max_len): - """ Remove excluded tags from data set, return normalized genre list. + ''' Remove excluded tags from data set, return normalized genre list. Filter all db tags, removing excluded tags supplied in opts. Test for multiple tags resolving to same normalized form. Normalized @@ -1198,13 +1198,13 @@ class CatalogBuilder: Return: genre_tags_dict (dict): dict of filtered, normalized tags in data set - """ + ''' def _format_tag_list(tags, indent=1, line_break=70, header='Tag list'): def _next_tag(sorted_tags): for (i, tag) in enumerate(sorted_tags): if i < len(tags) - 1: - yield tag + ", " + yield tag + ', ' else: yield tag @@ -1220,7 +1220,7 @@ class CatalogBuilder: return ans + out_str def _normalize_tag(tag, max_len): - """ Generate an XHTML-legal anchor string from tag. + ''' Generate an XHTML-legal anchor string from tag. Parse tag for non-ascii, convert to unicode name. @@ -1231,7 +1231,7 @@ class CatalogBuilder: Return: normalized (str): unicode names substituted for non-ascii chars, clipped to max_len - """ + ''' normalized = massaged = re.sub(r'\s', '', ascii_text(tag).lower()) if re.search(r'\W', normalized): @@ -1266,8 +1266,8 @@ class CatalogBuilder: " 'Comma separated text, like tags, shown in the browser',\n" " 'Text, column shown in the tag browser', or\n" " 'Text, but with a fixed set of permitted values'.") - self.opts.log.error("Eligible custom fields: %s" % ', '.join(eligible_custom_fields)) - raise InvalidGenresSourceFieldException("invalid custom field specified for genre_source_field") + self.opts.log.error('Eligible custom fields: %s' % ', '.join(eligible_custom_fields)) + raise InvalidGenresSourceFieldException('invalid custom field specified for genre_source_field') all_genre_tags = list(self.db.all_custom(self.db.field_metadata.key_to_label(self.opts.genre_source_field))) @@ -1282,7 +1282,7 @@ class CatalogBuilder: excluded_tags.append(tag) continue except: - self.opts.log.error("\tfilterDbTags(): malformed --exclude-genre regex pattern: %s" % self.opts.exclude_genre) + self.opts.log.error('\tfilterDbTags(): malformed --exclude-genre regex pattern: %s' % self.opts.exclude_genre) if tag == ' ': continue @@ -1299,15 +1299,15 @@ class CatalogBuilder: self.opts.log.warn(" Warning: multiple tags resolving to genre '%s':" % normalized) for key in genre_tags_dict: if genre_tags_dict[key] == normalized: - self.opts.log.warn(" %s" % key) + self.opts.log.warn(' %s' % key) if self.opts.verbose: - self.opts.log.info('%s' % _format_tag_list(genre_tags_dict, header="enabled genres")) - self.opts.log.info('%s' % _format_tag_list(excluded_tags, header="excluded genres")) + self.opts.log.info('%s' % _format_tag_list(genre_tags_dict, header='enabled genres')) + self.opts.log.info('%s' % _format_tag_list(excluded_tags, header='excluded genres')) return genre_tags_dict def filter_excluded_genres(self, tags, regex): - """ Remove excluded tags from a tag list + ''' Remove excluded tags from a tag list Run regex against list of tags, remove matching tags. Return filtered list. @@ -1316,7 +1316,7 @@ class CatalogBuilder: Return: tag_list(list): filtered list of tags - """ + ''' tag_list = [] @@ -1328,7 +1328,7 @@ class CatalogBuilder: else: tag_list.append(tag) except: - self.opts.log.error("\tfilter_excluded_genres(): malformed --exclude-genre regex pattern: %s" % regex) + self.opts.log.error('\tfilter_excluded_genres(): malformed --exclude-genre regex pattern: %s' % regex) return tags return tag_list @@ -1360,11 +1360,11 @@ class CatalogBuilder: return None def insert_prefix(self, soup, parent_tag, pos, prefix_char): - """ Generate HTML snippet with prefix character. + ''' Generate HTML snippet with prefix character. Insert a <code> snippet for Kindle, <span> snippet for EPUB. Optimized to preserve first-column alignment for MOBI, EPUB. - """ + ''' if self.opts.fmt == 'mobi': tag = soup.new_tag('code') else: @@ -1374,7 +1374,7 @@ class CatalogBuilder: parent_tag.insert(pos, tag) def generate_author_anchor(self, author): - """ Generate legal XHTML anchor. + ''' Generate legal XHTML anchor. Convert author to legal XHTML (may contain unicode chars), stripping non-alphanumeric chars. @@ -1384,8 +1384,8 @@ class CatalogBuilder: Return: (str): asciized version of author - """ - return re.sub(r"\W", "", ascii_text(author)) + ''' + return re.sub(r'\W', '', ascii_text(author)) def generate_format_args(self, book): """ Generate the format args for template substitution. @@ -1410,11 +1410,11 @@ class CatalogBuilder: rating=self.generate_rating_string(book), rating_parens='(%s)' % self.generate_rating_string(book) if 'rating' in book else '', pubyear=book['date'].split()[1] if book['date'] else '', - pubyear_parens="(%s)" % book['date'].split()[1] if book['date'] else '') + pubyear_parens='(%s)' % book['date'].split()[1] if book['date'] else '') return args def generate_html_by_author(self): - """ Generate content/ByAlphaAuthor.html. + ''' Generate content/ByAlphaAuthor.html. Loop through self.books_by_author, generate HTML with anchors for author and index letters. @@ -1424,17 +1424,17 @@ class CatalogBuilder: Output: content/ByAlphaAuthor.html (file) - """ + ''' - friendly_name = _("Authors") - self.update_progress_full_step("%s HTML" % friendly_name) + friendly_name = _('Authors') + self.update_progress_full_step('%s HTML' % friendly_name) soup = self.generate_html_empty_header(friendly_name) body = soup.find('body') btc = 0 - divTag = soup.new_tag("div") + divTag = soup.new_tag('div') dtc = 0 divOpeningTag = None dotc = 0 @@ -1468,11 +1468,11 @@ class CatalogBuilder: author_count = 0 divOpeningTag = soup.new_tag('div') if dtc > 0: - divOpeningTag['class'] = "initial_letter" + divOpeningTag['class'] = 'initial_letter' dotc = 0 - pIndexTag = soup.new_tag("p") - pIndexTag['class'] = "author_title_letter_index" - aTag = soup.new_tag("a") + pIndexTag = soup.new_tag('p') + pIndexTag['class'] = 'author_title_letter_index' + aTag = soup.new_tag('a') # current_letter = self.letter_or_symbol(book['author_sort'][0].upper()) current_letter = self.letter_or_symbol(sort_equivalents[idx]) if current_letter == self.SYMBOLS: @@ -1504,15 +1504,15 @@ class CatalogBuilder: dtc += 1 divRunningTag = soup.new_tag('div') - divRunningTag['class'] = "author_logical_group" + divRunningTag['class'] = 'author_logical_group' drtc = 0 non_series_books = 0 current_series = None - pAuthorTag = soup.new_tag("p") - pAuthorTag['class'] = "author_index" - aTag = soup.new_tag("a") - aTag['id'] = "%s" % self.generate_author_anchor(current_author) + pAuthorTag = soup.new_tag('p') + pAuthorTag['class'] = 'author_index' + aTag = soup.new_tag('a') + aTag['id'] = '%s' % self.generate_author_anchor(current_author) aTag.insert(0, NavigableString(current_author)) pAuthorTag.insert(0, aTag) if author_count == 1: @@ -1527,12 +1527,12 @@ class CatalogBuilder: # Start a new series current_series = book['series'] pSeriesTag = soup.new_tag('p') - pSeriesTag['class'] = "series" + pSeriesTag['class'] = 'series' if self.opts.fmt == 'mobi': - pSeriesTag['class'] = "series_mobi" + pSeriesTag['class'] = 'series_mobi' if self.opts.generate_series: aTag = soup.new_tag('a') - aTag['href'] = "{}.html#{}".format('BySeries', self.generate_series_anchor(book['series'])) + aTag['href'] = '{}.html#{}'.format('BySeries', self.generate_series_anchor(book['series'])) aTag.insert(0, book['series']) pSeriesTag.insert(0, aTag) else: @@ -1548,20 +1548,20 @@ class CatalogBuilder: current_series = None # Add books - pBookTag = soup.new_tag("p") - pBookTag['class'] = "line_item" + pBookTag = soup.new_tag('p') + pBookTag['class'] = 'line_item' ptc = 0 self.insert_prefix(soup, pBookTag, ptc, book['prefix']) ptc += 1 - spanTag = soup.new_tag("span") - spanTag['class'] = "entry" + spanTag = soup.new_tag('span') + spanTag['class'] = 'entry' stc = 0 - aTag = soup.new_tag("a") + aTag = soup.new_tag('a') if self.opts.generate_descriptions: - aTag['href'] = "book_%d.html" % (int(float(book['id']))) + aTag['href'] = 'book_%d.html' % (int(float(book['id']))) # Generate the title from the template args = self.generate_format_args(book) @@ -1593,7 +1593,7 @@ class CatalogBuilder: # loop ends here - pTag = soup.new_tag("p") + pTag = soup.new_tag('p') pTag['class'] = 'title' ptc = 0 aTag = soup.new_tag('a') @@ -1603,9 +1603,9 @@ class CatalogBuilder: if not self.generate_for_kindle_mobi: # Kindle don't need this because it shows section titles in Periodical format - aTag = soup.new_tag("a") + aTag = soup.new_tag('a') anchor_name = friendly_name.lower() - aTag['id'] = anchor_name.replace(" ", "") + aTag['id'] = anchor_name.replace(' ', '') pTag.insert(ptc, aTag) ptc += 1 pTag.insert(ptc, NavigableString('%s' % (friendly_name))) @@ -1624,13 +1624,13 @@ class CatalogBuilder: body.insert(btc, divTag) # Write the generated file to content_dir - outfile_spec = "%s/ByAlphaAuthor.html" % (self.content_dir) + outfile_spec = '%s/ByAlphaAuthor.html' % (self.content_dir) with open(outfile_spec, 'wb') as outfile: outfile.write(prettify(soup).encode('utf-8')) - self.html_filelist_1.append("content/ByAlphaAuthor.html") + self.html_filelist_1.append('content/ByAlphaAuthor.html') def generate_html_by_date_added(self): - """ Generate content/ByDateAdded.html. + ''' Generate content/ByDateAdded.html. Loop through self.books_to_catalog sorted by reverse date, generate HTML. @@ -1639,7 +1639,7 @@ class CatalogBuilder: Output: content/ByDateAdded.html (file) - """ + ''' def _add_books_to_html_by_month(this_months_list, dtc): if len(this_months_list): @@ -1651,10 +1651,10 @@ class CatalogBuilder: # Create a new month anchor date_string = strftime('%B %Y', current_date.timetuple()) - pIndexTag = soup.new_tag("p") - pIndexTag['class'] = "date_index" - aTag = soup.new_tag("a") - aTag['id'] = f"bda_{current_date.year}-{current_date.month}" + pIndexTag = soup.new_tag('p') + pIndexTag['class'] = 'date_index' + aTag = soup.new_tag('a') + aTag['id'] = f'bda_{current_date.year}-{current_date.month}' pIndexTag.insert(0, aTag) pIndexTag.insert(1, NavigableString(date_string)) divTag.insert(dtc, pIndexTag) @@ -1668,11 +1668,11 @@ class CatalogBuilder: current_author = new_entry['author'] non_series_books = 0 current_series = None - pAuthorTag = soup.new_tag("p") - pAuthorTag['class'] = "author_index" - aTag = soup.new_tag("a") + pAuthorTag = soup.new_tag('p') + pAuthorTag['class'] = 'author_index' + aTag = soup.new_tag('a') if self.opts.generate_authors: - aTag['href'] = "{}.html#{}".format("ByAlphaAuthor", self.generate_author_anchor(current_author)) + aTag['href'] = '{}.html#{}'.format('ByAlphaAuthor', self.generate_author_anchor(current_author)) aTag.insert(0, NavigableString(current_author)) pAuthorTag.insert(0, aTag) divTag.insert(dtc, pAuthorTag) @@ -1683,12 +1683,12 @@ class CatalogBuilder: # Start a new series current_series = new_entry['series'] pSeriesTag = soup.new_tag('p') - pSeriesTag['class'] = "series" + pSeriesTag['class'] = 'series' if self.opts.fmt == 'mobi': - pSeriesTag['class'] = "series_mobi" + pSeriesTag['class'] = 'series_mobi' if self.opts.generate_series: aTag = soup.new_tag('a') - aTag['href'] = "{}.html#{}".format('BySeries', self.generate_series_anchor(new_entry['series'])) + aTag['href'] = '{}.html#{}'.format('BySeries', self.generate_series_anchor(new_entry['series'])) aTag.insert(0, new_entry['series']) pSeriesTag.insert(0, aTag) else: @@ -1699,20 +1699,20 @@ class CatalogBuilder: current_series = None # Add books - pBookTag = soup.new_tag("p") - pBookTag['class'] = "line_item" + pBookTag = soup.new_tag('p') + pBookTag['class'] = 'line_item' ptc = 0 self.insert_prefix(soup, pBookTag, ptc, new_entry['prefix']) ptc += 1 - spanTag = soup.new_tag("span") - spanTag['class'] = "entry" + spanTag = soup.new_tag('span') + spanTag['class'] = 'entry' stc = 0 - aTag = soup.new_tag("a") + aTag = soup.new_tag('a') if self.opts.generate_descriptions: - aTag['href'] = "book_%d.html" % (int(float(new_entry['id']))) + aTag['href'] = 'book_%d.html' % (int(float(new_entry['id']))) # Generate the title from the template args = self.generate_format_args(new_entry) @@ -1740,10 +1740,10 @@ class CatalogBuilder: def _add_books_to_html_by_date_range(date_range_list, date_range, dtc): if len(date_range_list): - pIndexTag = soup.new_tag("p") - pIndexTag['class'] = "date_index" - aTag = soup.new_tag("a") - aTag['id'] = "bda_%s" % date_range.replace(' ', '') + pIndexTag = soup.new_tag('p') + pIndexTag['class'] = 'date_index' + aTag = soup.new_tag('a') + aTag['id'] = 'bda_%s' % date_range.replace(' ', '') pIndexTag.insert(0, aTag) pIndexTag.insert(1, NavigableString(date_range)) divTag.insert(dtc, pIndexTag) @@ -1751,20 +1751,20 @@ class CatalogBuilder: for new_entry in date_range_list: # Add books - pBookTag = soup.new_tag("p") - pBookTag['class'] = "line_item" + pBookTag = soup.new_tag('p') + pBookTag['class'] = 'line_item' ptc = 0 self.insert_prefix(soup, pBookTag, ptc, new_entry['prefix']) ptc += 1 - spanTag = soup.new_tag("span") - spanTag['class'] = "entry" + spanTag = soup.new_tag('span') + spanTag['class'] = 'entry' stc = 0 - aTag = soup.new_tag("a") + aTag = soup.new_tag('a') if self.opts.generate_descriptions: - aTag['href'] = "book_%d.html" % (int(float(new_entry['id']))) + aTag['href'] = 'book_%d.html' % (int(float(new_entry['id']))) # Generate the title from the template args = self.generate_format_args(new_entry) @@ -1783,14 +1783,14 @@ class CatalogBuilder: stc += 1 # Dot - spanTag.insert(stc, NavigableString(" · ")) + spanTag.insert(stc, NavigableString(' · ')) stc += 1 # Link to author - emTag = soup.new_tag("em") - aTag = soup.new_tag("a") + emTag = soup.new_tag('em') + aTag = soup.new_tag('a') if self.opts.generate_authors: - aTag['href'] = "{}.html#{}".format("ByAlphaAuthor", self.generate_author_anchor(new_entry['author'])) + aTag['href'] = '{}.html#{}'.format('ByAlphaAuthor', self.generate_author_anchor(new_entry['author'])) aTag.insert(0, NavigableString(new_entry['author'])) emTag.insert(0, aTag) spanTag.insert(stc, emTag) @@ -1803,15 +1803,15 @@ class CatalogBuilder: dtc += 1 return dtc - friendly_name = _("Recently Added") - self.update_progress_full_step("%s HTML" % friendly_name) + friendly_name = _('Recently Added') + self.update_progress_full_step('%s HTML' % friendly_name) soup = self.generate_html_empty_header(friendly_name) body = soup.find('body') btc = 0 - pTag = soup.new_tag("p") + pTag = soup.new_tag('p') pTag['class'] = 'title' ptc = 0 @@ -1822,9 +1822,9 @@ class CatalogBuilder: if not self.generate_for_kindle_mobi: # Kindle don't need this because it shows section titles in Periodical format - aTag = soup.new_tag("a") + aTag = soup.new_tag('a') anchor_name = friendly_name.lower() - aTag['id'] = anchor_name.replace(" ", "") + aTag['id'] = anchor_name.replace(' ', '') pTag.insert(ptc, aTag) ptc += 1 @@ -1833,7 +1833,7 @@ class CatalogBuilder: body.insert(btc, pTag) btc += 1 - divTag = soup.new_tag("div") + divTag = soup.new_tag('div') dtc = 0 # >>> Books by date range <<< @@ -1887,13 +1887,13 @@ class CatalogBuilder: body.insert(btc, divTag) # Write the generated file to content_dir - outfile_spec = "%s/ByDateAdded.html" % (self.content_dir) + outfile_spec = '%s/ByDateAdded.html' % (self.content_dir) with open(outfile_spec, 'wb') as outfile: outfile.write(prettify(soup).encode('utf-8')) - self.html_filelist_2.append("content/ByDateAdded.html") + self.html_filelist_2.append('content/ByDateAdded.html') def generate_html_by_date_read(self): - """ Generate content/ByDateRead.html. + ''' Generate content/ByDateRead.html. Create self.bookmarked_books_by_date_read from self.bookmarked_books. Loop through self.bookmarked_books_by_date_read, generate HTML. @@ -1903,46 +1903,46 @@ class CatalogBuilder: Output: content/ByDateRead.html (file) - """ + ''' def _add_books_to_html_by_day(todays_list, dtc): if len(todays_list): # Create a new day anchor date_string = strftime('%A, %B %d', current_date.timetuple()) - pIndexTag = soup.new_tag("p") - pIndexTag['class'] = "date_index" - aTag = soup.new_tag("a") - aTag['name'] = f"bdr_{current_date.year}-{current_date.month}-{current_date.day}" + pIndexTag = soup.new_tag('p') + pIndexTag['class'] = 'date_index' + aTag = soup.new_tag('a') + aTag['name'] = f'bdr_{current_date.year}-{current_date.month}-{current_date.day}' pIndexTag.insert(0, aTag) pIndexTag.insert(1, NavigableString(date_string)) divTag.insert(dtc, pIndexTag) dtc += 1 for new_entry in todays_list: - pBookTag = soup.new_tag("p") - pBookTag['class'] = "date_read" + pBookTag = soup.new_tag('p') + pBookTag['class'] = 'date_read' ptc = 0 # Percent read pBookTag.insert(ptc, NavigableString(new_entry['reading_progress'])) ptc += 1 - aTag = soup.new_tag("a") + aTag = soup.new_tag('a') if self.opts.generate_descriptions: - aTag['href'] = "book_%d.html" % (int(float(new_entry['id']))) + aTag['href'] = 'book_%d.html' % (int(float(new_entry['id']))) aTag.insert(0, NavigableString(new_entry['title'])) pBookTag.insert(ptc, aTag) ptc += 1 # Dot - pBookTag.insert(ptc, NavigableString(" · ")) + pBookTag.insert(ptc, NavigableString(' · ')) ptc += 1 # Link to author - emTag = soup.new_tag("em") - aTag = soup.new_tag("a") + emTag = soup.new_tag('em') + aTag = soup.new_tag('a') if self.opts.generate_authors: - aTag['href'] = "{}.html#{}".format("ByAlphaAuthor", self.generate_author_anchor(new_entry['author'])) + aTag['href'] = '{}.html#{}'.format('ByAlphaAuthor', self.generate_author_anchor(new_entry['author'])) aTag.insert(0, NavigableString(new_entry['author'])) emTag.insert(0, aTag) pBookTag.insert(ptc, emTag) @@ -1954,10 +1954,10 @@ class CatalogBuilder: def _add_books_to_html_by_date_range(date_range_list, date_range, dtc): if len(date_range_list): - pIndexTag = soup.new_tag("p") - pIndexTag['class'] = "date_index" - aTag = soup.new_tag("a") - aTag['name'] = "bdr_%s" % date_range.replace(' ', '') + pIndexTag = soup.new_tag('p') + pIndexTag['class'] = 'date_index' + aTag = soup.new_tag('a') + aTag['name'] = 'bdr_%s' % date_range.replace(' ', '') pIndexTag.insert(0, aTag) pIndexTag.insert(1, NavigableString(date_range)) divTag.insert(dtc, pIndexTag) @@ -1965,8 +1965,8 @@ class CatalogBuilder: for new_entry in date_range_list: # Add books - pBookTag = soup.new_tag("p") - pBookTag['class'] = "date_read" + pBookTag = soup.new_tag('p') + pBookTag['class'] = 'date_read' ptc = 0 # Percent read @@ -1976,22 +1976,22 @@ class CatalogBuilder: pBookTag.insert(ptc, NavigableString(f'{dot_string}{empty_dots}')) ptc += 1 - aTag = soup.new_tag("a") + aTag = soup.new_tag('a') if self.opts.generate_descriptions: - aTag['href'] = "book_%d.html" % (int(float(new_entry['id']))) + aTag['href'] = 'book_%d.html' % (int(float(new_entry['id']))) aTag.insert(0, NavigableString(new_entry['title'])) pBookTag.insert(ptc, aTag) ptc += 1 # Dot - pBookTag.insert(ptc, NavigableString(" · ")) + pBookTag.insert(ptc, NavigableString(' · ')) ptc += 1 # Link to author - emTag = soup.new_tag("em") - aTag = soup.new_tag("a") + emTag = soup.new_tag('em') + aTag = soup.new_tag('a') if self.opts.generate_authors: - aTag['href'] = "{}.html#{}".format("ByAlphaAuthor", self.generate_author_anchor(new_entry['author'])) + aTag['href'] = '{}.html#{}'.format('ByAlphaAuthor', self.generate_author_anchor(new_entry['author'])) aTag.insert(0, NavigableString(new_entry['author'])) emTag.insert(0, aTag) pBookTag.insert(ptc, emTag) @@ -2002,7 +2002,7 @@ class CatalogBuilder: return dtc friendly_name = _('Recently Read') - self.update_progress_full_step("%s HTML" % friendly_name) + self.update_progress_full_step('%s HTML' % friendly_name) if not self.bookmarked_books: return @@ -2019,13 +2019,13 @@ class CatalogBuilder: btc += 1 # Insert the anchor - aTag = soup.new_tag("a") + aTag = soup.new_tag('a') anchor_name = friendly_name.lower() - aTag['name'] = anchor_name.replace(" ", "") + aTag['name'] = anchor_name.replace(' ', '') body.insert(btc, aTag) btc += 1 - divTag = soup.new_tag("div") + divTag = soup.new_tag('div') dtc = 0 # self.bookmarked_books: (Bookmark, book) @@ -2063,13 +2063,13 @@ class CatalogBuilder: body.insert(btc, divTag) # Write the generated file to content_dir - outfile_spec = "%s/ByDateRead.html" % (self.content_dir) + outfile_spec = '%s/ByDateRead.html' % (self.content_dir) with open(outfile_spec, 'wb') as outfile: outfile.write(prettify(soup).encode('utf-8')) - self.html_filelist_2.append("content/ByDateRead.html") + self.html_filelist_2.append('content/ByDateRead.html') def generate_html_by_genres(self): - """ Generate individual HTML files per tag. + ''' Generate individual HTML files per tag. Filter out excluded tags. For each tag qualifying as a genre, create a separate HTML file. Normalize tags to flatten synonymous tags. @@ -2079,9 +2079,9 @@ class CatalogBuilder: Output: (files): HTML file per genre - """ + ''' - self.update_progress_full_step(_("Genres HTML")) + self.update_progress_full_step(_('Genres HTML')) # Extract books matching filtered_tags genre_list = [] @@ -2125,12 +2125,12 @@ class CatalogBuilder: if self.opts.verbose: if len(genre_list): - self.opts.log.info(" Genre summary: %d active genre tags used in generating catalog with %d titles" % + self.opts.log.info(' Genre summary: %d active genre tags used in generating catalog with %d titles' % (len(genre_list), len(self.books_to_catalog))) for genre in genre_list: for key in genre: - self.opts.log.info(" %s: %d %s" % (self.get_friendly_genre_tag(key), + self.opts.log.info(' %s: %d %s' % (self.get_friendly_genre_tag(key), len(genre[key]), 'titles' if len(genre[key]) > 1 else 'title')) @@ -2163,13 +2163,13 @@ class CatalogBuilder: books_by_current_author += 1 # Write the genre book list as an article - outfile = f"{self.content_dir}/Genre_{genre}.html" + outfile = f'{self.content_dir}/Genre_{genre}.html' titles_spanned = self.generate_html_by_genre(genre, True if index == 0 else False, genre_tag_set[genre], outfile) - tag_file = "content/Genre_%s.html" % genre + tag_file = 'content/Genre_%s.html' % genre master_genre_list.append({ 'tag': genre, 'file': tag_file, @@ -2180,7 +2180,7 @@ class CatalogBuilder: self.genres = master_genre_list def generate_html_by_genre(self, genre, section_head, books, outfile): - """ Generate individual genre HTML file. + ''' Generate individual genre HTML file. Generate an individual genre HTML file. Called from generate_html_by_genres() @@ -2195,7 +2195,7 @@ class CatalogBuilder: Returns: titles_spanned (list): [(first_author, first_book), (last_author, last_book)] - """ + ''' soup = self.generate_html_genre_header(genre) body = soup.find('body') @@ -2215,7 +2215,7 @@ class CatalogBuilder: # Create an anchor from the tag aTag = soup.new_tag('a') - aTag['id'] = "Genre_%s" % genre + aTag['id'] = 'Genre_%s' % genre divTag.insert(dtc, aTag) body.insert(btc, divTag) btc += 1 @@ -2235,11 +2235,11 @@ class CatalogBuilder: current_author = book['author'] non_series_books = 0 current_series = None - pAuthorTag = soup.new_tag("p") - pAuthorTag['class'] = "author_index" - aTag = soup.new_tag("a") + pAuthorTag = soup.new_tag('p') + pAuthorTag['class'] = 'author_index' + aTag = soup.new_tag('a') if self.opts.generate_authors: - aTag['href'] = "{}.html#{}".format("ByAlphaAuthor", self.generate_author_anchor(book['author'])) + aTag['href'] = '{}.html#{}'.format('ByAlphaAuthor', self.generate_author_anchor(book['author'])) aTag.insert(0, book['author']) pAuthorTag.insert(0, aTag) divTag.insert(dtc, pAuthorTag) @@ -2250,12 +2250,12 @@ class CatalogBuilder: # Start a new series current_series = book['series'] pSeriesTag = soup.new_tag('p') - pSeriesTag['class'] = "series" + pSeriesTag['class'] = 'series' if self.opts.fmt == 'mobi': - pSeriesTag['class'] = "series_mobi" + pSeriesTag['class'] = 'series_mobi' if self.opts.generate_series: aTag = soup.new_tag('a') - aTag['href'] = "{}.html#{}".format('BySeries', self.generate_series_anchor(book['series'])) + aTag['href'] = '{}.html#{}'.format('BySeries', self.generate_series_anchor(book['series'])) aTag.insert(0, book['series']) pSeriesTag.insert(0, aTag) else: @@ -2267,21 +2267,21 @@ class CatalogBuilder: current_series = None # Add books - pBookTag = soup.new_tag("p") - pBookTag['class'] = "line_item" + pBookTag = soup.new_tag('p') + pBookTag['class'] = 'line_item' ptc = 0 self.insert_prefix(soup, pBookTag, ptc, book['prefix']) ptc += 1 - spanTag = soup.new_tag("span") - spanTag['class'] = "entry" + spanTag = soup.new_tag('span') + spanTag['class'] = 'entry' stc = 0 # Add the book title - aTag = soup.new_tag("a") + aTag = soup.new_tag('a') if self.opts.generate_descriptions: - aTag['href'] = "book_%d.html" % (int(float(book['id']))) + aTag['href'] = 'book_%d.html' % (int(float(book['id']))) # Generate the title from the template args = self.generate_format_args(book) @@ -2321,7 +2321,7 @@ class CatalogBuilder: return titles_spanned def generate_html_by_series(self): - """ Generate content/BySeries.html. + ''' Generate content/BySeries.html. Search database for books in series. @@ -2331,9 +2331,9 @@ class CatalogBuilder: Output: content/BySeries.html (file) - """ + ''' friendly_name = ngettext('Series', 'Series', 2) - self.update_progress_full_step("%s HTML" % friendly_name) + self.update_progress_full_step('%s HTML' % friendly_name) self.opts.sort_by = 'series' @@ -2343,7 +2343,7 @@ class CatalogBuilder: if not self.books_by_series: self.opts.generate_series = False - self.opts.log(" no series found in selected books, skipping Series section") + self.opts.log(' no series found in selected books, skipping Series section') return # Generate series_sort @@ -2357,9 +2357,9 @@ class CatalogBuilder: body = soup.find('body') btc = 0 - divTag = soup.new_tag("div") + divTag = soup.new_tag('div') dtc = 0 - current_letter = "" + current_letter = '' current_series = None # Loop through books_by_series @@ -2369,15 +2369,15 @@ class CatalogBuilder: if self.letter_or_symbol(sort_equivalents[idx]) != current_letter: # Start a new letter with Index letter current_letter = self.letter_or_symbol(sort_equivalents[idx]) - pIndexTag = soup.new_tag("p") - pIndexTag['class'] = "series_letter_index" - aTag = soup.new_tag("a") + pIndexTag = soup.new_tag('p') + pIndexTag['class'] = 'series_letter_index' + aTag = soup.new_tag('a') if current_letter == self.SYMBOLS: - aTag['id'] = self.SYMBOLS + "_series" + aTag['id'] = self.SYMBOLS + '_series' pIndexTag.insert(0, aTag) pIndexTag.insert(1, NavigableString(self.SYMBOLS)) else: - aTag['id'] = self.generate_unicode_name(current_letter) + "_series" + aTag['id'] = self.generate_unicode_name(current_letter) + '_series' pIndexTag.insert(0, aTag) pIndexTag.insert(1, NavigableString(sort_equivalents[idx])) divTag.insert(dtc, pIndexTag) @@ -2388,9 +2388,9 @@ class CatalogBuilder: series_count += 1 current_series = book['series'] pSeriesTag = soup.new_tag('p') - pSeriesTag['class'] = "series" + pSeriesTag['class'] = 'series' if self.opts.fmt == 'mobi': - pSeriesTag['class'] = "series_mobi" + pSeriesTag['class'] = 'series_mobi' aTag = soup.new_tag('a') aTag['id'] = self.generate_series_anchor(book['series']) pSeriesTag.insert(0, aTag) @@ -2399,21 +2399,21 @@ class CatalogBuilder: dtc += 1 # Add books - pBookTag = soup.new_tag("p") - pBookTag['class'] = "line_item" + pBookTag = soup.new_tag('p') + pBookTag['class'] = 'line_item' ptc = 0 book['prefix'] = self.discover_prefix(book) self.insert_prefix(soup, pBookTag, ptc, book['prefix']) ptc += 1 - spanTag = soup.new_tag("span") - spanTag['class'] = "entry" + spanTag = soup.new_tag('span') + spanTag['class'] = 'entry' stc = 0 - aTag = soup.new_tag("a") + aTag = soup.new_tag('a') if self.opts.generate_descriptions: - aTag['href'] = "book_%d.html" % (int(float(book['id']))) + aTag['href'] = 'book_%d.html' % (int(float(book['id']))) # Use series, series index if avail else just title # aTag.insert(0,'%d. %s · %s' % (book['series_index'],escape(book['title']), ' & '.join(book['authors']))) @@ -2432,9 +2432,9 @@ class CatalogBuilder: stc += 1 # Link to author - aTag = soup.new_tag("a") + aTag = soup.new_tag('a') if self.opts.generate_authors: - aTag['href'] = "{}.html#{}".format("ByAlphaAuthor", + aTag['href'] = '{}.html#{}'.format('ByAlphaAuthor', self.generate_author_anchor(' & '.join(book['authors']))) aTag.insert(0, NavigableString(' & '.join(book['authors']))) spanTag.insert(stc, aTag) @@ -2446,7 +2446,7 @@ class CatalogBuilder: divTag.insert(dtc, pBookTag) dtc += 1 - pTag = soup.new_tag("p") + pTag = soup.new_tag('p') pTag['class'] = 'title' ptc = 0 aTag = soup.new_tag('a') @@ -2456,9 +2456,9 @@ class CatalogBuilder: if not self.generate_for_kindle_mobi: # Insert the <h2> tag with book_count at the head - aTag = soup.new_tag("a") + aTag = soup.new_tag('a') anchor_name = friendly_name.lower() - aTag['id'] = anchor_name.replace(" ", "") + aTag['id'] = anchor_name.replace(' ', '') pTag.insert(0, aTag) pTag.insert(1, NavigableString('%s' % friendly_name)) body.insert(btc, pTag) @@ -2468,13 +2468,13 @@ class CatalogBuilder: body.insert(btc, divTag) # Write the generated file to content_dir - outfile_spec = "%s/BySeries.html" % (self.content_dir) + outfile_spec = '%s/BySeries.html' % (self.content_dir) with open(outfile_spec, 'wb') as outfile: outfile.write(prettify(soup).encode('utf-8')) - self.html_filelist_1.append("content/BySeries.html") + self.html_filelist_1.append('content/BySeries.html') def generate_html_by_title(self): - """ Generate content/ByAlphaTitle.html. + ''' Generate content/ByAlphaTitle.html. Generate HTML of books sorted by title. @@ -2483,15 +2483,15 @@ class CatalogBuilder: Output: content/ByAlphaTitle.html (file) - """ + ''' - self.update_progress_full_step(_("Titles HTML")) + self.update_progress_full_step(_('Titles HTML')) - soup = self.generate_html_empty_header("Books By Alpha Title") + soup = self.generate_html_empty_header('Books By Alpha Title') body = soup.find('body') btc = 0 - pTag = soup.new_tag("p") + pTag = soup.new_tag('p') pTag['class'] = 'title' ptc = 0 aTag = soup.new_tag('a') @@ -2501,8 +2501,8 @@ class CatalogBuilder: if not self.generate_for_kindle_mobi: # Kindle don't need this because it shows section titles in Periodical format - aTag = soup.new_tag("a") - aTag['id'] = "bytitle" + aTag = soup.new_tag('a') + aTag['id'] = 'bytitle' pTag.insert(ptc, aTag) ptc += 1 pTag.insert(ptc, NavigableString(_('Titles'))) @@ -2510,9 +2510,9 @@ class CatalogBuilder: body.insert(btc, pTag) btc += 1 - divTag = soup.new_tag("div") + divTag = soup.new_tag('div') dtc = 0 - current_letter = "" + current_letter = '' # Re-sort title list without leading series/series_index # Incoming title <series> <series_index>: <title> @@ -2541,39 +2541,39 @@ class CatalogBuilder: dtc += 1 divRunningTag = soup.new_tag('div') if dtc > 0: - divRunningTag['class'] = "initial_letter" + divRunningTag['class'] = 'initial_letter' drtc = 0 - pIndexTag = soup.new_tag("p") - pIndexTag['class'] = "author_title_letter_index" - aTag = soup.new_tag("a") + pIndexTag = soup.new_tag('p') + pIndexTag['class'] = 'author_title_letter_index' + aTag = soup.new_tag('a') current_letter = self.letter_or_symbol(sort_equivalents[idx]) if current_letter == self.SYMBOLS: - aTag['id'] = self.SYMBOLS + "_titles" + aTag['id'] = self.SYMBOLS + '_titles' pIndexTag.insert(0, aTag) pIndexTag.insert(1, NavigableString(self.SYMBOLS)) else: - aTag['id'] = self.generate_unicode_name(current_letter) + "_titles" + aTag['id'] = self.generate_unicode_name(current_letter) + '_titles' pIndexTag.insert(0, aTag) pIndexTag.insert(1, NavigableString(sort_equivalents[idx])) divRunningTag.insert(dtc, pIndexTag) drtc += 1 # Add books - pBookTag = soup.new_tag("p") - pBookTag['class'] = "line_item" + pBookTag = soup.new_tag('p') + pBookTag['class'] = 'line_item' ptc = 0 self.insert_prefix(soup, pBookTag, ptc, book['prefix']) ptc += 1 - spanTag = soup.new_tag("span") - spanTag['class'] = "entry" + spanTag = soup.new_tag('span') + spanTag['class'] = 'entry' stc = 0 # Link to book - aTag = soup.new_tag("a") + aTag = soup.new_tag('a') if self.opts.generate_descriptions: - aTag['href'] = "book_%d.html" % (int(float(book['id']))) + aTag['href'] = 'book_%d.html' % (int(float(book['id']))) # Generate the title from the template args = self.generate_format_args(book) @@ -2592,14 +2592,14 @@ class CatalogBuilder: stc += 1 # Dot - spanTag.insert(stc, NavigableString(" · ")) + spanTag.insert(stc, NavigableString(' · ')) stc += 1 # Link to author - emTag = soup.new_tag("em") - aTag = soup.new_tag("a") + emTag = soup.new_tag('em') + aTag = soup.new_tag('a') if self.opts.generate_authors: - aTag['href'] = "{}.html#{}".format("ByAlphaAuthor", self.generate_author_anchor(book['author'])) + aTag['href'] = '{}.html#{}'.format('ByAlphaAuthor', self.generate_author_anchor(book['author'])) aTag.insert(0, NavigableString(book['author'])) emTag.insert(0, aTag) spanTag.insert(stc, emTag) @@ -2622,13 +2622,13 @@ class CatalogBuilder: btc += 1 # Write the volume to content_dir - outfile_spec = "%s/ByAlphaTitle.html" % (self.content_dir) + outfile_spec = '%s/ByAlphaTitle.html' % (self.content_dir) with open(outfile_spec, 'wb') as outfile: outfile.write(prettify(soup).encode('utf-8')) - self.html_filelist_1.append("content/ByAlphaTitle.html") + self.html_filelist_1.append('content/ByAlphaTitle.html') def generate_html_description_header(self, book): - """ Generate the HTML Description header from template. + ''' Generate the HTML Description header from template. Create HTML Description from book metadata and template. Called by generate_html_descriptions() @@ -2638,7 +2638,7 @@ class CatalogBuilder: Return: soup (BeautifulSoup): HTML Description for book - """ + ''' from calibre.ebooks.oeb.base import XHTML_NS @@ -2687,11 +2687,11 @@ class CatalogBuilder: author = book['author'] if book['prefix']: - author_prefix = book['prefix'] + ' ' + _("by ") + author_prefix = book['prefix'] + ' ' + _('by ') elif self.opts.connected_kindle and book['id'] in self.bookmarked_books: - author_prefix = self.SYMBOL_READING + ' ' + _("by ") + author_prefix = self.SYMBOL_READING + ' ' + _('by ') else: - author_prefix = _("by ") + author_prefix = _('by ') # Genres genres = '' @@ -2703,7 +2703,7 @@ class CatalogBuilder: aTag = _soup.new_tag('a') if self.opts.generate_genres: try: - aTag['href'] = "Genre_%s.html" % self.genre_tags_dict[tag] + aTag['href'] = 'Genre_%s.html' % self.genre_tags_dict[tag] except KeyError: pass aTag.insert(0, NavigableString(tag)) @@ -2733,12 +2733,12 @@ class CatalogBuilder: # Thumb _soup = BeautifulSoup('<html>', selfClosingTags=['img']) - thumb = _soup.new_tag("img") + thumb = _soup.new_tag('img') if 'cover' in book and book['cover']: - thumb['src'] = "../images/thumbnail_%d.jpg" % int(book['id']) + thumb['src'] = '../images/thumbnail_%d.jpg' % int(book['id']) else: - thumb['src'] = "../images/thumbnail_default.jpg" - thumb['alt'] = "cover thumbnail" + thumb['src'] = '../images/thumbnail_default.jpg' + thumb['alt'] = 'cover thumbnail' # Publisher publisher = ' ' @@ -2772,8 +2772,8 @@ class CatalogBuilder: body = soup.find('body') btc = 0 # Insert the title anchor for inbound links - aTag = soup.new_tag("a") - aTag['id'] = "book%d" % int(book['id']) + aTag = soup.new_tag('a') + aTag['id'] = 'book%d' % int(book['id']) divTag = soup.new_tag('div') divTag.insert(0, aTag) body.insert(btc, divTag) @@ -2784,14 +2784,14 @@ class CatalogBuilder: if aTag: if book['series']: if self.opts.generate_series: - aTag['href'] = "{}.html#{}".format('BySeries', self.generate_series_anchor(book['series'])) + aTag['href'] = '{}.html#{}'.format('BySeries', self.generate_series_anchor(book['series'])) else: aTag.extract() # Insert the author link aTag = body.find('a', attrs={'class': 'author'}) if self.opts.generate_authors and aTag: - aTag['href'] = "{}.html#{}".format("ByAlphaAuthor", + aTag['href'] = '{}.html#{}'.format('ByAlphaAuthor', self.generate_author_anchor(book['author'])) if publisher == ' ': @@ -2823,7 +2823,7 @@ class CatalogBuilder: return soup def generate_html_descriptions(self): - """ Generate Description HTML for each book. + ''' Generate Description HTML for each book. Loop though books, write Description HTML for each book. @@ -2832,13 +2832,13 @@ class CatalogBuilder: Output: (files): Description HTML for each book - """ + ''' - self.update_progress_full_step(_("Descriptions HTML")) + self.update_progress_full_step(_('Descriptions HTML')) for (title_num, title) in enumerate(self.books_by_title): - self.update_progress_micro_step("%s %d of %d" % - (_("Description HTML"), + self.update_progress_micro_step('%s %d of %d' % + (_('Description HTML'), title_num, len(self.books_by_title)), float(title_num * 100 / len(self.books_by_title)) / 100) @@ -2846,11 +2846,11 @@ class CatalogBuilder: soup = self.generate_html_description_header(title) # Write the book entry to content_dir - with open("%s/book_%d.html" % (self.content_dir, int(title['id'])), 'wb') as outfile: + with open('%s/book_%d.html' % (self.content_dir, int(title['id'])), 'wb') as outfile: outfile.write(prettify(soup).encode('utf-8')) def generate_html_empty_header(self, title): - """ Return a boilerplate HTML header. + ''' Return a boilerplate HTML header. Generate an HTML header with document title. @@ -2859,7 +2859,7 @@ class CatalogBuilder: Return: soup (BeautifulSoup): HTML header with document title inserted - """ + ''' header = ''' <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> @@ -2880,7 +2880,7 @@ class CatalogBuilder: return soup def generate_html_genre_header(self, title): - """ Generate HTML header with initial body content + ''' Generate HTML header with initial body content Start with a generic HTML header, add <p> and <div> @@ -2889,7 +2889,7 @@ class CatalogBuilder: Return: soup (BeautifulSoup): HTML with initial <p> and <div> tags - """ + ''' soup = self.generate_html_empty_header(title) bodyTag = soup.find('body') @@ -2902,7 +2902,7 @@ class CatalogBuilder: return soup def generate_masthead_image(self, out_path): - """ Generate a Kindle masthead image. + ''' Generate a Kindle masthead image. Generate a Kindle masthead image, used with Kindle periodical format. @@ -2915,7 +2915,7 @@ class CatalogBuilder: Output: out_path (file): masthead image (GIF) - """ + ''' from calibre.ebooks.conversion.config import load_defaults @@ -2953,7 +2953,7 @@ class CatalogBuilder: img.save(f, 'GIF') def generate_ncx_header(self): - """ Generate the basic NCX file. + ''' Generate the basic NCX file. Generate the initial NCX, which is added to depending on included Sections. @@ -2965,9 +2965,9 @@ class CatalogBuilder: Outputs: ncx_root (file): NCX foundation - """ + ''' - self.update_progress_full_step(_("NCX header")) + self.update_progress_full_step(_('NCX header')) header = ''' <ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" xmlns:calibre="http://calibre.kovidgoyal.net/2009/metadata" version="2005-1" xml:lang="en"> @@ -2981,23 +2981,23 @@ class CatalogBuilder: # Build a top-level navPoint for Kindle periodicals navPointTag = makeelement('navPoint', navMapTag, class_='periodical', id='title', playOrder=self.play_order) self.play_order += 1 - makeelement('{http://calibre.kovidgoyal.net/2009/metadata}meta-img', navPointTag, id="mastheadImage", src="images/mastheadImage.gif") + makeelement('{http://calibre.kovidgoyal.net/2009/metadata}meta-img', navPointTag, id='mastheadImage', src='images/mastheadImage.gif') navLabelTag = makeelement('navLabel', navPointTag) makeelement('text', navLabelTag).text = self.opts.catalog_title if self.opts.generate_authors: - makeelement('content', navPointTag, src="content/ByAlphaAuthor.html") + makeelement('content', navPointTag, src='content/ByAlphaAuthor.html') elif self.opts.generate_titles: - makeelement('content', navPointTag, src="content/ByAlphaTitle.html") + makeelement('content', navPointTag, src='content/ByAlphaTitle.html') elif self.opts.generate_series: - makeelement('content', navPointTag, src="content/BySeries.html") + makeelement('content', navPointTag, src='content/BySeries.html') elif self.opts.generate_genres: - makeelement('content', navPointTag, src="%s" % self.genres[0]['file']) + makeelement('content', navPointTag, src='%s' % self.genres[0]['file']) elif self.opts.generate_recently_added: - makeelement('content', navPointTag, src="content/ByDateAdded.html") + makeelement('content', navPointTag, src='content/ByDateAdded.html') elif self.opts.generate_descriptions: # Descriptions only - makeelement('content', navPointTag, src="content/book_%d.html" % int(self.books_by_description[0]['id'])) + makeelement('content', navPointTag, src='content/book_%d.html' % int(self.books_by_description[0]['id'])) def generate_ncx_section_header(self, section_id, section_header, content_src): root = self.ncx_root @@ -3018,18 +3018,18 @@ class CatalogBuilder: def generate_ncx_subsection(self, navPointTag, section_id, section_text, content_src, cm_tags={}): navPointVolumeTag = makeelement('navPoint', navPointTag, id=section_id, playOrder=self.play_order) if self.generate_for_kindle_mobi: - navPointVolumeTag.set('class', "article") + navPointVolumeTag.set('class', 'article') self.play_order += 1 - navLabelTag = makeelement("navLabel", navPointVolumeTag) - makeelement("text", navLabelTag).text = section_text - makeelement("content", navPointVolumeTag, src=content_src) + navLabelTag = makeelement('navLabel', navPointVolumeTag) + makeelement('text', navLabelTag).text = section_text + makeelement('content', navPointVolumeTag, src=content_src) if self.generate_for_kindle_mobi: for name, text in cm_tags.items(): makeelement('{http://calibre.kovidgoyal.net/2009/metadata}meta', navPointVolumeTag, name=name).text = text def generate_ncx_descriptions(self, tocTitle): - """ Add Descriptions to the basic NCX file. + ''' Add Descriptions to the basic NCX file. Generate the Descriptions NCX content, add to self.ncx_soup. @@ -3041,20 +3041,20 @@ class CatalogBuilder: Outputs: ncx_soup (file): updated - """ + ''' section_header = '%s [%d]' % (tocTitle, len(self.books_by_description)) if self.generate_for_kindle_mobi: section_header = tocTitle - navPointTag = self.generate_ncx_section_header('bydescription-ID', section_header, "content/book_%d.html" % int(self.books_by_description[0]['id'])) + navPointTag = self.generate_ncx_section_header('bydescription-ID', section_header, 'content/book_%d.html' % int(self.books_by_description[0]['id'])) - self.update_progress_full_step(_("NCX for descriptions")) + self.update_progress_full_step(_('NCX for descriptions')) # --- Construct the 'Descriptions' section --- # Add the section navPoint # Loop over the titles for book in self.books_by_description: - sec_id = "book%dID" % int(book['id']) + sec_id = 'book%dID' % int(book['id']) if book['series']: series_index = str(book['series_index']) if series_index.endswith('.0'): @@ -3085,7 +3085,7 @@ class CatalogBuilder: sec_text = self.format_ncx_text('%s · %s' % (book['title'], book['author']), dest='title') - content_src="content/book_%d.html#book%d" % (int(book['id']), int(book['id'])) + content_src='content/book_%d.html#book%d' % (int(book['id']), int(book['id'])) cm_tags = {} if book['date']: @@ -3104,7 +3104,7 @@ class CatalogBuilder: self.generate_ncx_subsection(navPointTag, sec_id, sec_text, content_src, cm_tags) def generate_ncx_by_series(self, tocTitle): - """ Add Series to the basic NCX file. + ''' Add Series to the basic NCX file. Generate the Series NCX content, add to self.ncx_soup. @@ -3116,21 +3116,21 @@ class CatalogBuilder: Outputs: ncx_soup (file): updated - """ + ''' - self.update_progress_full_step(_("NCX for Series")) + self.update_progress_full_step(_('NCX for Series')) def _add_to_series_by_letter(current_series_list): - current_series_list = " • ".join(current_series_list) - current_series_list = self.format_ncx_text(current_series_list, dest="description") + current_series_list = ' • '.join(current_series_list) + current_series_list = self.format_ncx_text(current_series_list, dest='description') series_by_letter.append(current_series_list) # --- Construct the 'Books By Series' section --- section_header = '%s [%d]' % (tocTitle, len(self.all_series)) if self.generate_for_kindle_mobi: section_header = tocTitle - output = "BySeries" - navPointTag = self.generate_ncx_section_header('byseries-ID', section_header, "content/%s.html#section_start" % (output)) + output = 'BySeries' + navPointTag = self.generate_ncx_section_header('byseries-ID', section_header, 'content/%s.html#section_start' % (output)) series_by_letter = [] # Establish initial letter equivalencies @@ -3146,7 +3146,7 @@ class CatalogBuilder: title_letters = [current_letter] current_series_list = [] - current_series = "" + current_series = '' for idx, book in enumerate(title_list): sort_title = self.generate_sort_title(book['series']) self.establish_equivalencies([sort_title])[0] @@ -3171,22 +3171,22 @@ class CatalogBuilder: # Add *article* entries for each populated series title letter for (i, books) in enumerate(series_by_letter): - sec_id = "%sSeries-ID" % (title_letters[i].upper()) + sec_id = '%sSeries-ID' % (title_letters[i].upper()) if len(title_letters[i]) > 1: - fmt_string = _("Series beginning with %s") + fmt_string = _('Series beginning with %s') else: fmt_string = _("Series beginning with '%s'") sec_text = fmt_string % (title_letters[i] if len(title_letters[i]) > 1 else title_letters[i]) if title_letters[i] == self.SYMBOLS: - content_src = f"content/{output}.html#{self.SYMBOLS}_series" + content_src = f'content/{output}.html#{self.SYMBOLS}_series' else: - content_src = f"content/{output}.html#{self.generate_unicode_name(title_letters[i])}_series" + content_src = f'content/{output}.html#{self.generate_unicode_name(title_letters[i])}_series' cm_tags = {'description': self.format_ncx_text(books, dest='description')} self.generate_ncx_subsection(navPointTag, sec_id, sec_text, content_src, cm_tags) def generate_ncx_by_title(self, tocTitle): - """ Add Titles to the basic NCX file. + ''' Add Titles to the basic NCX file. Generate the Titles NCX content, add to self.ncx_soup. @@ -3198,21 +3198,21 @@ class CatalogBuilder: Outputs: ncx_soup (file): updated - """ + ''' - self.update_progress_full_step(_("NCX for Titles")) + self.update_progress_full_step(_('NCX for Titles')) def _add_to_books_by_letter(current_book_list): - current_book_list = " • ".join(current_book_list) - current_book_list = self.format_ncx_text(current_book_list, dest="description") + current_book_list = ' • '.join(current_book_list) + current_book_list = self.format_ncx_text(current_book_list, dest='description') books_by_letter.append(current_book_list) # --- Construct the 'Books By Title' section --- section_header = '%s [%d]' % (tocTitle, len(self.books_by_title)) if self.generate_for_kindle_mobi: section_header = tocTitle - output = "ByAlphaTitle" - navPointTag = self.generate_ncx_section_header("byalphatitle-ID", section_header, "content/%s.html#section_start" % (output)) + output = 'ByAlphaTitle' + navPointTag = self.generate_ncx_section_header('byalphatitle-ID', section_header, 'content/%s.html#section_start' % (output)) books_by_letter = [] @@ -3230,7 +3230,7 @@ class CatalogBuilder: current_letter = self.letter_or_symbol(sort_equivalents[0]) title_letters = [current_letter] current_book_list = [] - current_book = "" + current_book = '' for idx, book in enumerate(title_list): # if self.letter_or_symbol(book['title_sort'][0]) != current_letter: if self.letter_or_symbol(sort_equivalents[idx]) != current_letter: @@ -3255,22 +3255,22 @@ class CatalogBuilder: # Add *article* entries for each populated title letter for (i, books) in enumerate(books_by_letter): - sec_id = "%sTitles-ID" % (title_letters[i].upper()) + sec_id = '%sTitles-ID' % (title_letters[i].upper()) if len(title_letters[i]) > 1: - fmt_string = _("Titles beginning with %s") + fmt_string = _('Titles beginning with %s') else: fmt_string = _("Titles beginning with '%s'") sec_text = fmt_string % (title_letters[i] if len(title_letters[i]) > 1 else title_letters[i]) if title_letters[i] == self.SYMBOLS: - content_src = f"content/{output}.html#{self.SYMBOLS}_titles" + content_src = f'content/{output}.html#{self.SYMBOLS}_titles' else: - content_src = f"content/{output}.html#{self.generate_unicode_name(title_letters[i])}_titles" + content_src = f'content/{output}.html#{self.generate_unicode_name(title_letters[i])}_titles' cm_tags = {'description': self.format_ncx_text(books, dest='description')} self.generate_ncx_subsection(navPointTag, sec_id, sec_text, content_src, cm_tags) def generate_ncx_by_author(self, tocTitle): - """ Add Authors to the basic NCX file. + ''' Add Authors to the basic NCX file. Generate the Authors NCX content, add to self.ncx_soup. @@ -3282,24 +3282,24 @@ class CatalogBuilder: Outputs: ncx_soup (file): updated - """ + ''' - self.update_progress_full_step(_("NCX for Authors")) + self.update_progress_full_step(_('NCX for Authors')) def _add_to_author_list(current_author_list, current_letter): - current_author_list = " • ".join(current_author_list) - current_author_list = self.format_ncx_text(current_author_list, dest="description") + current_author_list = ' • '.join(current_author_list) + current_author_list = self.format_ncx_text(current_author_list, dest='description') master_author_list.append((current_author_list, current_letter)) - HTML_file = "content/ByAlphaAuthor.html" + HTML_file = 'content/ByAlphaAuthor.html' # --- Construct the 'Books By Author' *section* --- - file_ID = "%s" % tocTitle.lower() - file_ID = file_ID.replace(" ", "") + file_ID = '%s' % tocTitle.lower() + file_ID = file_ID.replace(' ', '') section_header = '%s [%d]' % (tocTitle, len(self.individual_authors)) if self.generate_for_kindle_mobi: section_header = tocTitle - navPointTag = self.generate_ncx_section_header("%s-ID" % file_ID, section_header, "%s#section_start" % HTML_file) + navPointTag = self.generate_ncx_section_header('%s-ID' % file_ID, section_header, '%s#section_start' % HTML_file) # Create an NCX article entry for each populated author index letter # Loop over the sorted_authors list, find start of each letter, @@ -3332,22 +3332,22 @@ class CatalogBuilder: # Add *article* entries for each populated author initial letter # master_author_list{}: [0]:author list [1]:Initial letter for authors_by_letter in master_author_list: - sec_id = "%sauthors-ID" % (authors_by_letter[1]) + sec_id = '%sauthors-ID' % (authors_by_letter[1]) if authors_by_letter[1] == self.SYMBOLS: - fmt_string = _("Authors beginning with %s") + fmt_string = _('Authors beginning with %s') else: fmt_string = _("Authors beginning with '%s'") sec_text = fmt_string % authors_by_letter[1] if authors_by_letter[1] == self.SYMBOLS: - content_src = f"{HTML_file}#{authors_by_letter[1]}_authors" + content_src = f'{HTML_file}#{authors_by_letter[1]}_authors' else: - content_src = f"{HTML_file}#{self.generate_unicode_name(authors_by_letter[1])}_authors" + content_src = f'{HTML_file}#{self.generate_unicode_name(authors_by_letter[1])}_authors' cm_tags = {'description': authors_by_letter[0]} self.generate_ncx_subsection(navPointTag, sec_id, sec_text, content_src, cm_tags) def generate_ncx_by_date_added(self, tocTitle): - """ Add Recently Added to the basic NCX file. + ''' Add Recently Added to the basic NCX file. Generate the Recently Added NCX content, add to self.ncx_soup. @@ -3359,28 +3359,28 @@ class CatalogBuilder: Outputs: ncx_soup (file): updated - """ + ''' - self.update_progress_full_step(_("NCX for Recently Added")) + self.update_progress_full_step(_('NCX for Recently Added')) def _add_to_master_month_list(current_titles_list): book_count = len(current_titles_list) - current_titles_list = " • ".join(current_titles_list) + current_titles_list = ' • '.join(current_titles_list) current_titles_list = self.format_ncx_text(current_titles_list, dest='description') master_month_list.append((current_titles_list, current_date, book_count)) def _add_to_master_date_range_list(current_titles_list): book_count = len(current_titles_list) - current_titles_list = " • ".join(current_titles_list) + current_titles_list = ' • '.join(current_titles_list) current_titles_list = self.format_ncx_text(current_titles_list, dest='description') master_date_range_list.append((current_titles_list, date_range, book_count)) - HTML_file = "content/ByDateAdded.html" + HTML_file = 'content/ByDateAdded.html' # --- Construct the 'Recently Added' *section* --- - file_ID = "%s" % tocTitle.lower() - file_ID = file_ID.replace(" ", "") - navPointTag = self.generate_ncx_section_header("%s-ID" % file_ID, tocTitle, "%s#section_start" % HTML_file) + file_ID = '%s' % tocTitle.lower() + file_ID = file_ID.replace(' ', '') + navPointTag = self.generate_ncx_section_header('%s-ID' % file_ID, tocTitle, '%s#section_start' % HTML_file) # Create an NCX article entry for each date range current_titles_list = [] @@ -3407,9 +3407,9 @@ class CatalogBuilder: # Add *article* entries for each populated date range # master_date_range_list{}: [0]:titles list [1]:datestr for books_by_date_range in master_date_range_list: - sec_id = "%s-ID" % books_by_date_range[1].replace(' ', '') + sec_id = '%s-ID' % books_by_date_range[1].replace(' ', '') sec_text = books_by_date_range[1] - content_src = "{}#bda_{}".format(HTML_file, + content_src = '{}#bda_{}'.format(HTML_file, books_by_date_range[1].replace(' ', '')) navStr = '%d titles' % books_by_date_range[2] if books_by_date_range[2] > 1 else \ '%d title' % books_by_date_range[2] @@ -3443,9 +3443,9 @@ class CatalogBuilder: # master_months_list{}: [0]:titles list [1]:date for books_by_month in master_month_list: datestr = strftime('%B %Y', books_by_month[1].timetuple()) - sec_id = f"bda_{books_by_month[1].year}-{books_by_month[1].month}-ID" + sec_id = f'bda_{books_by_month[1].year}-{books_by_month[1].month}-ID' sec_text = datestr - content_src = "{}#bda_{}-{}".format(HTML_file, + content_src = '{}#bda_{}-{}'.format(HTML_file, books_by_month[1].year, books_by_month[1].month) navStr = '%d titles' % books_by_month[2] if books_by_month[2] > 1 else \ '%d title' % books_by_month[2] @@ -3453,7 +3453,7 @@ class CatalogBuilder: self.generate_ncx_subsection(navPointTag, sec_id, sec_text, content_src, cm_tags) def generate_ncx_by_date_read(self, tocTitle): - """ Add By Date Read to the basic NCX file. + ''' Add By Date Read to the basic NCX file. Generate the By Date Read NCX content (Kindle only), add to self.ncx_soup. @@ -3465,31 +3465,31 @@ class CatalogBuilder: Outputs: ncx_soup (file): updated - """ + ''' def _add_to_master_day_list(current_titles_list): book_count = len(current_titles_list) - current_titles_list = " • ".join(current_titles_list) + current_titles_list = ' • '.join(current_titles_list) current_titles_list = self.format_ncx_text(current_titles_list, dest='description') master_day_list.append((current_titles_list, current_date, book_count)) def _add_to_master_date_range_list(current_titles_list): book_count = len(current_titles_list) - current_titles_list = " • ".join(current_titles_list) + current_titles_list = ' • '.join(current_titles_list) current_titles_list = self.format_ncx_text(current_titles_list, dest='description') master_date_range_list.append((current_titles_list, date_range, book_count)) - self.update_progress_full_step(_("NCX for Recently Read")) + self.update_progress_full_step(_('NCX for Recently Read')) if not self.bookmarked_books_by_date_read: return - HTML_file = "content/ByDateRead.html" + HTML_file = 'content/ByDateRead.html' # --- Construct the 'Recently Read' *section* --- - file_ID = "%s" % tocTitle.lower() - file_ID = file_ID.replace(" ", "") - navPointTag = self.generate_ncx_section_header("%s-ID" % file_ID, tocTitle, "%s#section_start" % HTML_file) + file_ID = '%s' % tocTitle.lower() + file_ID = file_ID.replace(' ', '') + navPointTag = self.generate_ncx_section_header('%s-ID' % file_ID, tocTitle, '%s#section_start' % HTML_file) # Create an NCX article entry for each date range current_titles_list = [] @@ -3542,11 +3542,11 @@ class CatalogBuilder: # master_day_list{}: [0]:titles list [1]:date for books_by_day in master_day_list: datestr = strftime('%A, %B %d', books_by_day[1].timetuple()) - sec_id = "bdr_{}-{}-{}ID".format(books_by_day[1].year, + sec_id = 'bdr_{}-{}-{}ID'.format(books_by_day[1].year, books_by_day[1].month, books_by_day[1].day) sec_text = datestr - content_src = "{}#bdr_{}-{}-{}".format(HTML_file, + content_src = '{}#bdr_{}-{}-{}'.format(HTML_file, books_by_day[1].year, books_by_day[1].month, books_by_day[1].day) @@ -3556,7 +3556,7 @@ class CatalogBuilder: self.generate_ncx_subsection(navPointTag, sec_id, sec_text, content_src, cm_tags) def generate_ncx_by_genre(self, tocTitle): - """ Add Genres to the basic NCX file. + ''' Add Genres to the basic NCX file. Generate the Genre NCX content, add to self.ncx_soup. @@ -3568,26 +3568,26 @@ class CatalogBuilder: Outputs: ncx_soup (file): updated - """ + ''' - self.update_progress_full_step(_("NCX for genres")) + self.update_progress_full_step(_('NCX for genres')) if not len(self.genres): - self.opts.log.warn(" No genres found\n" - " No Genre section added to Catalog") + self.opts.log.warn(' No genres found\n' + ' No Genre section added to Catalog') return # --- Construct the 'Books By Genre' *section* --- - file_ID = "%s" % tocTitle.lower() - file_ID = file_ID.replace(" ", "") + file_ID = '%s' % tocTitle.lower() + file_ID = file_ID.replace(' ', '') section_header = '%s [%d]' % (tocTitle, len(self.genres)) if self.generate_for_kindle_mobi: section_header = tocTitle - navPointTag = self.generate_ncx_section_header("%s-ID" % file_ID, section_header, "content/Genre_%s.html#section_start" % self.genres[0]['tag']) + navPointTag = self.generate_ncx_section_header('%s-ID' % file_ID, section_header, 'content/Genre_%s.html#section_start' % self.genres[0]['tag']) for genre in self.genres: # Add an article for each genre - sec_id = "genre-%s-ID" % genre['tag'] + sec_id = 'genre-%s-ID' % genre['tag'] # GwR *** Can this be optimized? normalized_tag = None for friendly_tag in self.genre_tags_dict: @@ -3595,16 +3595,16 @@ class CatalogBuilder: normalized_tag = self.genre_tags_dict[friendly_tag] break sec_text = self.format_ncx_text(NavigableString(friendly_tag), dest='description') - content_src = f"content/Genre_{normalized_tag}.html#Genre_{normalized_tag}" + content_src = f'content/Genre_{normalized_tag}.html#Genre_{normalized_tag}' if len(genre['titles_spanned']) > 1: - author_range = "{} - {}".format(genre['titles_spanned'][0][0], genre['titles_spanned'][1][0]) + author_range = '{} - {}'.format(genre['titles_spanned'][0][0], genre['titles_spanned'][1][0]) else: - author_range = "%s" % (genre['titles_spanned'][0][0]) + author_range = '%s' % (genre['titles_spanned'][0][0]) titles = [] for title in genre['books']: titles.append(title['title']) titles = sorted(titles, key=lambda x: (self.generate_sort_title(x), self.generate_sort_title(x))) - titles_list = self.generate_short_description(" • ".join(titles), dest="description") + titles_list = self.generate_short_description(' • '.join(titles), dest='description') cm_tags = {'author': author_range, 'description': self.format_ncx_text(titles_list, dest='description')} self.generate_ncx_subsection(navPointTag, sec_id, sec_text, content_src, cm_tags) @@ -3626,7 +3626,7 @@ class CatalogBuilder: opts.basename + '.opf' (file): written """ - self.update_progress_full_step(_("Generating OPF")) + self.update_progress_full_step(_('Generating OPF')) lang = get_lang() or 'en' if lang_as_iso639_1(lang): lang = lang_as_iso639_1(lang) @@ -3648,7 +3648,7 @@ class CatalogBuilder: title=prepare_string_for_xml(self.opts.catalog_title), creator=prepare_string_for_xml(self.opts.creator), lang=prepare_string_for_xml(lang), - pt="periodical:default" if self.generate_for_kindle_mobi else "" + pt='periodical:default' if self.generate_for_kindle_mobi else '' ) root = safe_xml_fromstring(header) manifest = root.xpath('//*[local-name()="manifest"]')[0] @@ -3662,40 +3662,40 @@ class CatalogBuilder: makeelement('itemref', spine, idref=id) return ans - manifest_item(id='ncx', href='%s.ncx' % self.opts.basename, media_type="application/x-dtbncx+xml") + manifest_item(id='ncx', href='%s.ncx' % self.opts.basename, media_type='application/x-dtbncx+xml') manifest_item(id='stylesheet', href=self.stylesheet, media_type='text/css') if self.generate_for_kindle_mobi: - manifest_item('mastheadimage-image', "images/mastheadImage.gif", 'image/gif') + manifest_item('mastheadimage-image', 'images/mastheadImage.gif', 'image/gif') # Write the thumbnail images, descriptions to the manifest if self.opts.generate_descriptions: for thumb in self.thumbs: end = thumb.find('.jpg') - manifest_item("%s-image" % thumb[:end], "images/%s" % (thumb), 'image/jpeg') + manifest_item('%s-image' % thumb[:end], 'images/%s' % (thumb), 'image/jpeg') # Add html_files to manifest and spine for file in self.html_filelist_1: # By Author, By Title, By Series, start = file.find('/') + 1 end = file.find('.') - manifest_item(file[start:end].lower(), file, "application/xhtml+xml", add_to_spine=True) + manifest_item(file[start:end].lower(), file, 'application/xhtml+xml', add_to_spine=True) # Add genre files to manifest and spine for genre in self.genres: start = genre['file'].find('/') + 1 end = genre['file'].find('.') - manifest_item(genre['file'][start:end].lower(), genre['file'], "application/xhtml+xml", add_to_spine=True) + manifest_item(genre['file'][start:end].lower(), genre['file'], 'application/xhtml+xml', add_to_spine=True) for file in self.html_filelist_2: # By Date Added, By Date Read start = file.find('/') + 1 end = file.find('.') - manifest_item(file[start:end].lower(), file, "application/xhtml+xml", add_to_spine=True) + manifest_item(file[start:end].lower(), file, 'application/xhtml+xml', add_to_spine=True) for book in self.books_by_description: # manifest - manifest_item("book%d" % int(book['id']), "content/book_%d.html" % int(book['id']), "application/xhtml+xml", add_to_spine=True) + manifest_item('book%d' % int(book['id']), 'content/book_%d.html' % int(book['id']), 'application/xhtml+xml', add_to_spine=True) # Guide if self.generate_for_kindle_mobi: @@ -3704,11 +3704,11 @@ class CatalogBuilder: # Write the OPF file pretty_opf(root), pretty_xml_tree(root) output = etree.tostring(root, encoding='utf-8') - with open(f"{self.catalog_path}/{self.opts.basename}.opf", 'wb') as outfile: + with open(f'{self.catalog_path}/{self.opts.basename}.opf', 'wb') as outfile: outfile.write(output.strip()) def generate_rating_string(self, book): - """ Generate rating string for Descriptions. + ''' Generate rating string for Descriptions. Starting with database rating (0-10), return 5 stars, with 0-5 filled, balance empty. @@ -3718,7 +3718,7 @@ class CatalogBuilder: Return: rating (str): 5 stars, 1-5 solid, balance empty. Empty str for no rating. - """ + ''' rating = '' try: @@ -3734,7 +3734,7 @@ class CatalogBuilder: return rating def generate_series_anchor(self, series): - """ Generate legal XHTML anchor for series names. + ''' Generate legal XHTML anchor for series names. Flatten series name to ascii_legal text. @@ -3743,16 +3743,16 @@ class CatalogBuilder: Return: (str): asciized version of series name - """ + ''' # Generate a legal XHTML id/href string if self.letter_or_symbol(series) == self.SYMBOLS: - return "symbol_%s_series" % re.sub(r'\W', '', series).lower() + return 'symbol_%s_series' % re.sub(r'\W', '', series).lower() else: - return "%s_series" % re.sub(r'\W', '', ascii_text(series)).lower() + return '%s_series' % re.sub(r'\W', '', ascii_text(series)).lower() def generate_short_description(self, description, dest=None): - """ Generate a truncated version of the supplied string. + ''' Generate a truncated version of the supplied string. Given a string and NCX destination, truncate string to length specified in self.opts. @@ -3766,15 +3766,15 @@ class CatalogBuilder: Return: (str): truncated description - """ + ''' def _short_description(description, limit): - short_description = "" + short_description = '' words = description.split() for word in words: - short_description += word + " " + short_description += word + ' ' if len(short_description) > limit: - short_description += "..." + short_description += '...' return short_description if not description: @@ -3855,7 +3855,7 @@ class CatalogBuilder: return ' '.join(translated) def generate_thumbnail(self, title, image_dir, thumb_file): - """ Create thumbnail of cover or return previously cached thumb. + ''' Create thumbnail of cover or return previously cached thumb. Test thumb archive for currently cached cover. Return cached version, or create and cache new version. @@ -3868,7 +3868,7 @@ class CatalogBuilder: Output: (file): thumb written to /images (archive): current thumb archived under cover crc - """ + ''' from calibre.utils.img import scale_image def _open_archive(mode='r'): @@ -3918,7 +3918,7 @@ class CatalogBuilder: zf.writestr(uuid + cover_crc, thumb_data) def generate_thumbnails(self): - """ Generate a thumbnail cover for each book. + ''' Generate a thumbnail cover for each book. Generate or retrieve a thumbnail for each cover. If nonexistent or faulty cover data, substitute default cover. Checks for updated default cover. @@ -3929,15 +3929,15 @@ class CatalogBuilder: Output: thumbs (list): list of referenced thumbnails - """ + ''' - self.update_progress_full_step(_("Thumbnails")) + self.update_progress_full_step(_('Thumbnails')) thumbs = ['thumbnail_default.jpg'] - image_dir = "%s/images" % self.catalog_path + image_dir = '%s/images' % self.catalog_path for (i, title) in enumerate(self.books_by_title): # Update status - self.update_progress_micro_step("%s %d of %d" % - (_("Thumbnail"), i, len(self.books_by_title)), + self.update_progress_micro_step('%s %d of %d' % + (_('Thumbnail'), i, len(self.books_by_title)), i / float(len(self.books_by_title))) thumb_file = 'thumbnail_%d.jpg' % int(title['id']) @@ -3945,7 +3945,7 @@ class CatalogBuilder: valid_cover = True try: self.generate_thumbnail(title, image_dir, thumb_file) - thumbs.append("thumbnail_%d.jpg" % int(title['id'])) + thumbs.append('thumbnail_%d.jpg' % int(title['id'])) except: if 'cover' in title and os.path.exists(title['cover']): valid_cover = False @@ -3960,8 +3960,8 @@ class CatalogBuilder: if not thumb_generated: self.opts.log.warn(" using default cover for '%s' (%d)" % (title['title'], title['id'])) # Confirm thumb exists, default is current - default_thumb_fp = os.path.join(image_dir, "thumbnail_default.jpg") - cover = os.path.join(self.catalog_path, "DefaultCover.png") + default_thumb_fp = os.path.join(image_dir, 'thumbnail_default.jpg') + cover = os.path.join(self.catalog_path, 'DefaultCover.png') title['cover'] = cover if not os.path.exists(cover): @@ -3975,14 +3975,14 @@ class CatalogBuilder: thumb_timestamp = os.path.getmtime(default_thumb_fp) if thumb_timestamp < cover_timestamp: if self.DEBUG and self.opts.verbose: - self.opts.log.warn("updating thumbnail_default for %s" % title['title']) + self.opts.log.warn('updating thumbnail_default for %s' % title['title']) self.generate_thumbnail(title, image_dir, - "thumbnail_default.jpg" if valid_cover else thumb_file) + 'thumbnail_default.jpg' if valid_cover else thumb_file) else: if self.DEBUG and self.opts.verbose: - self.opts.log.warn(" generating new thumbnail_default.jpg") + self.opts.log.warn(' generating new thumbnail_default.jpg') self.generate_thumbnail(title, image_dir, - "thumbnail_default.jpg" if valid_cover else thumb_file) + 'thumbnail_default.jpg' if valid_cover else thumb_file) # Clear the book's cover property title['cover'] = None @@ -3998,7 +3998,7 @@ class CatalogBuilder: self.thumbs = thumbs def generate_unicode_name(self, c): - """ Generate a legal XHTML anchor from unicode character. + ''' Generate a legal XHTML anchor from unicode character. Generate a legal XHTML anchor from unicode character. @@ -4007,13 +4007,13 @@ class CatalogBuilder: Return: (str): legal XHTML anchor string of unicode character name - """ + ''' fullname = ''.join(unicodedata.name(str(cc)) for cc in c) terms = fullname.split() - return "_".join(terms) + return '_'.join(terms) def get_excluded_tags(self): - """ Get excluded_tags from opts.exclusion_rules. + ''' Get excluded_tags from opts.exclusion_rules. Parse opts.exclusion_rules for tags to be excluded, return list. Log books that will be excluded by excluded_tags. @@ -4023,7 +4023,7 @@ class CatalogBuilder: Return: excluded_tags (list): excluded tags - """ + ''' excluded_tags = [] for rule in self.opts.exclusion_rules: if rule[1] == _('Tags'): @@ -4034,7 +4034,7 @@ class CatalogBuilder: # Report excluded books if excluded_tags: - self.opts.log.info(" Books excluded by tag:") + self.opts.log.info(' Books excluded by tag:') data = self.db.get_data_as_dict(ids=self.opts.ids) for record in data: matched = list(set(record['tags']) & set(excluded_tags)) @@ -4047,7 +4047,7 @@ class CatalogBuilder: return excluded_tags def get_friendly_genre_tag(self, genre): - """ Return the first friendly_tag matching genre. + ''' Return the first friendly_tag matching genre. Scan self.genre_tags_dict[] for first friendly_tag matching genre. genre_tags_dict[] populated in filter_genre_tags(). @@ -4057,21 +4057,21 @@ class CatalogBuilder: Return: friendly_tag (str): friendly_tag matching genre - """ + ''' # Find the first instance of friendly_tag matching genre for friendly_tag in self.genre_tags_dict: if self.genre_tags_dict[friendly_tag] == genre: return friendly_tag def get_output_profile(self, _opts): - """ Return profile matching opts.output_profile + ''' Return profile matching opts.output_profile Input: _opts (object): build options object Return: (profile): output profile matching name - """ + ''' for profile in output_profiles(): if profile.short_name == _opts.output_profile: return profile @@ -4099,12 +4099,12 @@ class CatalogBuilder: prefix_rule['prefix'] = rule[3] pr.append(prefix_rule) except: - self.opts.log.error("malformed prefix_rules: %s" % repr(self.opts.prefix_rules)) + self.opts.log.error('malformed prefix_rules: %s' % repr(self.opts.prefix_rules)) raise return pr def letter_or_symbol(self, char): - """ Test asciized char for A-z. + ''' Test asciized char for A-z. Convert char to ascii, test for A-z. @@ -4113,14 +4113,14 @@ class CatalogBuilder: Return: (str): char if A-z, else SYMBOLS - """ + ''' if not re.search('[a-zA-Z]', ascii_text(char)): return self.SYMBOLS else: return char def load_section_templates(self): - """ Add section templates to local namespace. + ''' Add section templates to local namespace. Load section templates from resource directory. If user has made local copies, these will be used for individual section generation. @@ -4133,7 +4133,7 @@ class CatalogBuilder: Results: (strs): section templates added to local namespace - """ + ''' for line in P('catalog/section_list_templates.conf', data=True).decode('utf-8').splitlines(): line = line.lstrip() @@ -4146,7 +4146,7 @@ class CatalogBuilder: setattr(self, key, val) def merge_comments(self, record): - """ Merge comments with custom column content. + ''' Merge comments with custom column content. Merge comments from book metadata with user-specified custom column content, optionally before or after. Optionally insert <hr> between @@ -4157,7 +4157,7 @@ class CatalogBuilder: Return: merged (str): comments merged with addendum - """ + ''' merged = '' if record['description']: @@ -4194,7 +4194,7 @@ class CatalogBuilder: return merged def process_exclusions(self, data_set): - """ Filter data_set based on exclusion_rules. + ''' Filter data_set based on exclusion_rules. Compare each book in data_set to each exclusion_rule. Remove books matching exclusion criteria. @@ -4204,7 +4204,7 @@ class CatalogBuilder: Return: (list): filtered data_set - """ + ''' filtered_data_set = [] exclusion_pairs = [] exclusion_set = [] @@ -4217,7 +4217,7 @@ class CatalogBuilder: continue if exclusion_pairs: if self.opts.verbose: - self.opts.log.info(" Books excluded by custom field contents:") + self.opts.log.info(' Books excluded by custom field contents:') for record in data_set: for exclusion_pair in exclusion_pairs: @@ -4272,7 +4272,7 @@ class CatalogBuilder: return data_set def relist_multiple_authors(self, books_by_author): - """ Create multiple entries for books with multiple authors + ''' Create multiple entries for books with multiple authors Given a list of books by author, scan list for books with multiple authors. Add a cloned copy of the book per additional author. @@ -4284,7 +4284,7 @@ class CatalogBuilder: Return: (list): books_by_author with additional cloned entries for books with multiple authors - """ + ''' multiple_author_books = [] @@ -4327,11 +4327,11 @@ class CatalogBuilder: self.progress_int = 0.01 self.reporter(self.progress_int, self.progress_string) if self.opts.cli_environment: - log_msg = f"{self.progress_int * 100:3.0f}% {self.progress_string}" + log_msg = f'{self.progress_int * 100:3.0f}% {self.progress_string}' if self.opts.verbose: - log_msg += " (%s)" % str(datetime.timedelta(seconds=int(time.time() - self.opts.start_time))) + log_msg += ' (%s)' % str(datetime.timedelta(seconds=int(time.time() - self.opts.start_time))) else: - log_msg = ("{} ({})".format(self.progress_string, + log_msg = ('{} ({})'.format(self.progress_string, str(datetime.timedelta(seconds=int(time.time() - self.opts.start_time))))) self.opts.log(log_msg) @@ -4358,7 +4358,7 @@ class CatalogBuilder: self.reporter(self.progress_int, self.progress_string) def write_ncx(self): - """ Write accumulated ncx_soup to file. + ''' Write accumulated ncx_soup to file. Expanded description @@ -4368,10 +4368,10 @@ class CatalogBuilder: Output: (file): basename.NCX written - """ + ''' - self.update_progress_full_step(_("Saving NCX")) + self.update_progress_full_step(_('Saving NCX')) pretty_xml_tree(self.ncx_root) ncx = etree.tostring(self.ncx_root, encoding='utf-8') - with open(f"{self.catalog_path}/{self.opts.basename}.ncx", 'wb') as outfile: + with open(f'{self.catalog_path}/{self.opts.basename}.ncx', 'wb') as outfile: outfile.write(ncx) diff --git a/src/calibre/library/catalogs/utils.py b/src/calibre/library/catalogs/utils.py index b28ac28d02..5e7441c1e6 100644 --- a/src/calibre/library/catalogs/utils.py +++ b/src/calibre/library/catalogs/utils.py @@ -19,11 +19,11 @@ class NumberToText: # {{{ 4:56 => four fifty-six ''' ORDINALS = ['zeroth','first','second','third','fourth','fifth','sixth','seventh','eighth','ninth'] - lessThanTwenty = ["<zero>","one","two","three","four","five","six","seven","eight","nine", - "ten","eleven","twelve","thirteen","fourteen","fifteen","sixteen","seventeen", - "eighteen","nineteen"] - tens = ["<zero>","<tens>","twenty","thirty","forty","fifty","sixty","seventy","eighty","ninety"] - hundreds = ["<zero>","one","two","three","four","five","six","seven","eight","nine"] + lessThanTwenty = ['<zero>','one','two','three','four','five','six','seven','eight','nine', + 'ten','eleven','twelve','thirteen','fourteen','fifteen','sixteen','seventeen', + 'eighteen','nineteen'] + tens = ['<zero>','<tens>','twenty','thirty','forty','fifty','sixty','seventy','eighty','ninety'] + hundreds = ['<zero>','one','two','three','four','five','six','seven','eight','nine'] def __init__(self, number, verbose=False): self.number = number @@ -37,31 +37,31 @@ class NumberToText: # {{{ # Convert intToTranslate to string # intToTranslate is a three-digit number - tensComponentString = "" + tensComponentString = '' hundredsComponent = intToTranslate - (intToTranslate % 100) tensComponent = intToTranslate % 100 # Build the hundreds component if hundredsComponent: - hundredsComponentString = "%s hundred" % self.hundreds[hundredsComponent//100] + hundredsComponentString = '%s hundred' % self.hundreds[hundredsComponent//100] else: - hundredsComponentString = "" + hundredsComponentString = '' # Build the tens component if tensComponent < 20: tensComponentString = self.lessThanTwenty[tensComponent] else: - tensPart = "" - onesPart = "" + tensPart = '' + onesPart = '' # Get the tens part tensPart = self.tens[tensComponent // 10] onesPart = self.lessThanTwenty[tensComponent % 10] if intToTranslate % 10: - tensComponentString = f"{tensPart}-{onesPart}" + tensComponentString = f'{tensPart}-{onesPart}' else: - tensComponentString = "%s" % tensPart + tensComponentString = '%s' % tensPart # Concatenate the results result = '' @@ -70,21 +70,21 @@ class NumberToText: # {{{ elif not hundredsComponent and tensComponent: result = tensComponentString elif hundredsComponent and tensComponent: - result = hundredsComponentString + " " + tensComponentString + result = hundredsComponentString + ' ' + tensComponentString else: - prints(" NumberToText.stringFromInt(): empty result translating %d" % intToTranslate) + prints(' NumberToText.stringFromInt(): empty result translating %d' % intToTranslate) return result def numberTranslate(self): hundredsNumber = 0 thousandsNumber = 0 - hundredsString = "" - thousandsString = "" - resultString = "" + hundredsString = '' + thousandsString = '' + resultString = '' self.suffix = '' if self.verbose: - self.log("numberTranslate(): %s" % self.number) + self.log('numberTranslate(): %s' % self.number) # Special case ordinals if re.search('[st|nd|rd|th]',self.number): @@ -92,7 +92,7 @@ class NumberToText: # {{{ ordinal_suffix = re.search(r'[\D]', self.number) ordinal_number = re.sub(r'\D','',re.sub(',','',self.number)) if self.verbose: - self.log("Ordinal: %s" % ordinal_number) + self.log('Ordinal: %s' % ordinal_number) self.number_as_float = ordinal_number self.suffix = self.number[ordinal_suffix.start():] if int(ordinal_number) > 9: @@ -104,9 +104,9 @@ class NumberToText: # {{{ # Test for time elif re.search(':',self.number): if self.verbose: - self.log("Time: %s" % self.number) + self.log('Time: %s' % self.number) self.number_as_float = re.sub(':','.',self.number) - time_strings = self.number.split(":") + time_strings = self.number.split(':') hours = NumberToText(time_strings[0]).text minutes = NumberToText(time_strings[1]).text self.text = f'{hours.capitalize()}-{minutes}' @@ -114,16 +114,16 @@ class NumberToText: # {{{ # Test for % elif re.search('%', self.number): if self.verbose: - self.log("Percent: %s" % self.number) + self.log('Percent: %s' % self.number) self.number_as_float = self.number.split('%')[0] self.text = NumberToText(self.number.replace('%',' percent')).text # Test for decimal elif re.search('\\.',self.number): if self.verbose: - self.log("Decimal: %s" % self.number) + self.log('Decimal: %s' % self.number) self.number_as_float = self.number - decimal_strings = self.number.split(".") + decimal_strings = self.number.split('.') left = NumberToText(decimal_strings[0]).text right = NumberToText(decimal_strings[1]).text self.text = f'{left.capitalize()} point {right}' @@ -131,7 +131,7 @@ class NumberToText: # {{{ # Test for hyphenated elif re.search('-', self.number): if self.verbose: - self.log("Hyphenated: %s" % self.number) + self.log('Hyphenated: %s' % self.number) self.number_as_float = self.number.split('-')[0] strings = self.number.split('-') if re.search('[0-9]+', strings[0]): @@ -145,14 +145,14 @@ class NumberToText: # {{{ # Test for only commas and numbers elif re.search(',', self.number) and not re.search('[^0-9,]',self.number): if self.verbose: - self.log("Comma(s): %s" % self.number) + self.log('Comma(s): %s' % self.number) self.number_as_float = re.sub(',','',self.number) self.text = NumberToText(self.number_as_float).text # Test for hybrid e.g., 'K2, 2nd, 10@10' elif re.search('[\\D]+', self.number): if self.verbose: - self.log("Hybrid: %s" % self.number) + self.log('Hybrid: %s' % self.number) # Split the token into number/text number_position = re.search(r'\d',self.number).start() text_position = re.search(r'\D',self.number).start() @@ -167,7 +167,7 @@ class NumberToText: # {{{ else: if self.verbose: - self.log("Clean: %s" % self.number) + self.log('Clean: %s' % self.number) try: self.float_as_number = float(self.number) number = int(self.number) @@ -175,18 +175,18 @@ class NumberToText: # {{{ return if number > 10**9: - self.text = "%d out of range" % number + self.text = '%d out of range' % number return if number == 10**9: - self.text = "one billion" + self.text = 'one billion' else : # Isolate the three-digit number groups millionsNumber = number//10**6 thousandsNumber = (number - (millionsNumber * 10**6))//10**3 hundredsNumber = number - (millionsNumber * 10**6) - (thousandsNumber * 10**3) if self.verbose: - print(f"Converting {millionsNumber} {thousandsNumber} {hundredsNumber}") + print(f'Converting {millionsNumber} {thousandsNumber} {hundredsNumber}') # Convert hundredsNumber if hundredsNumber : @@ -209,16 +209,16 @@ class NumberToText: # {{{ # Concatenate the strings resultString = '' if millionsNumber: - resultString += "%s million " % millionsString + resultString += '%s million ' % millionsString if thousandsNumber: - resultString += "%s thousand " % thousandsString + resultString += '%s thousand ' % thousandsString if hundredsNumber: - resultString += "%s" % hundredsString + resultString += '%s' % hundredsString if not millionsNumber and not thousandsNumber and not hundredsNumber: - resultString = "zero" + resultString = 'zero' if self.verbose: self.log('resultString: %s' % resultString) diff --git a/src/calibre/library/coloring.py b/src/calibre/library/coloring.py index e6c491295f..255cd08730 100644 --- a/src/calibre/library/coloring.py +++ b/src/calibre/library/coloring.py @@ -114,9 +114,9 @@ class Rule: # {{{ def ondevice_condition(self, col, action, val): if action == 'is set': - return "ondevice()" + return 'ondevice()' if action == 'is not set': - return "!ondevice()" + return '!ondevice()' def bool_condition(self, col, action, val): test = {'is true': '0, 0, 1', @@ -129,9 +129,9 @@ class Rule: # {{{ def number_condition(self, col, action, val): if action == 'is set': - return f"${col}" + return f'${col}' if action == 'is not set': - return f"!${col}" + return f'!${col}' lt, eq, gt = { 'eq': ('', '1', ''), 'lt': ('1', '', ''), @@ -144,9 +144,9 @@ class Rule: # {{{ def rating_condition(self, col, action, val): if action == 'is set': - return f"${col}" + return f'${col}' if action == 'is not set': - return f"!${col}" + return f'!${col}' lt, eq, gt = { 'eq': ('', '1', ''), 'lt': ('1', '', ''), @@ -176,9 +176,9 @@ class Rule: # {{{ "format_date(today(), 'yyyy-MM-dd')), '1', '', ''), '')") %(col, val, col)) if action == 'is set': - return (f"${col}") + return (f'${col}') if action == 'is not set': - return (f"!${col}") + return (f'!${col}') if action == 'is today': return f"substr(format_date(raw_field('{col}'), 'iso'), 0, 10) == substr(today(), 0, 10)" lt, eq, gt = { @@ -192,9 +192,9 @@ class Rule: # {{{ if not sep or sep == '|': sep = ',' if action == 'is set': - return f"${col}" + return f'${col}' if action == 'is not set': - return f"!${col}" + return f'!${col}' if action == 'has': return "str_in_list(field('%s'), '%s', \"%s\", '1', '')"%(col, sep, val) if action == 'does not have': @@ -206,9 +206,9 @@ class Rule: # {{{ def text_condition(self, col, action, val): if action == 'is set': - return f"${col}" + return f'${col}' if action == 'is not set': - return f"!${col}" + return f'!${col}' if action == 'is': return "strcmp(field('%s'), \"%s\", '', '1', '')"%(col, val) if action == 'is not': diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 602415bfdc..0d54712301 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -1402,8 +1402,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): raise NoSuchFormat('Record %d has no %s file'%(id_, fmt)) if windows_atomic_move is not None: if not isinstance(dest, string_or_bytes): - raise Exception("Error, you must pass the dest as a path when" - " using windows_atomic_move") + raise Exception('Error, you must pass the dest as a path when' + ' using windows_atomic_move') if dest: if samefile(path, dest): # Ensure that the file has the same case as dest @@ -1458,8 +1458,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): path = os.path.join(self.library_path, self.path(id, index_is_id=True), 'cover.jpg') if windows_atomic_move is not None: if not isinstance(dest, string_or_bytes): - raise Exception("Error, you must pass the dest as a path when" - " using windows_atomic_move") + raise Exception('Error, you must pass the dest as a path when' + ' using windows_atomic_move') if os.access(path, os.R_OK) and dest and not samefile(dest, path): windows_atomic_move.copy_path_to(path, dest) return True @@ -2030,7 +2030,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): else: use_sort_as_name = False is_editable = (category not in ['news', 'rating', 'languages'] and - datatype != "composite") + datatype != 'composite') categories[category] = [tag_class(formatter(r.n), count=r.c, id=r.id, avg=avgr(r), sort=r.s, category=category, diff --git a/src/calibre/library/prefs.py b/src/calibre/library/prefs.py index 08f6b9daa8..fd7d3c19c1 100644 --- a/src/calibre/library/prefs.py +++ b/src/calibre/library/prefs.py @@ -85,7 +85,7 @@ class DBPrefs(dict): data = json.dumps(self, indent=2, default=to_json) if not isinstance(data, bytes): data = data.encode('utf-8') - with open(to_filename, "wb") as f: + with open(to_filename, 'wb') as f: f.write(data) except: import traceback @@ -96,7 +96,7 @@ class DBPrefs(dict): try: from_filename = os.path.join(library_path, 'metadata_db_prefs_backup.json') - with open(from_filename, "rb") as f: + with open(from_filename, 'rb') as f: d = json.load(f, object_hook=from_json) if not recreate_prefs: return d diff --git a/src/calibre/libunzip.py b/src/calibre/libunzip.py index 0138826a21..60015d0010 100644 --- a/src/calibre/libunzip.py +++ b/src/calibre/libunzip.py @@ -42,9 +42,9 @@ def update(pathtozip, patterns, filepaths, names, compression=zipfile.ZIP_DEFLAT def extract(filename, dir): - """ + ''' Extract archive C{filename} into directory C{dir} - """ + ''' zf = zipfile.ZipFile(filename) zf.extractall(dir) diff --git a/src/calibre/linux.py b/src/calibre/linux.py index 390ae31c48..39ec4ca80c 100644 --- a/src/calibre/linux.py +++ b/src/calibre/linux.py @@ -287,7 +287,7 @@ class ZshCompleter: # {{{ if opt.dest in {'extract_to', 'debug_pipeline', 'to_dir', 'outbox', 'with_library', 'library_path'}: arg += "'_path_files -/'" elif opt.choices: - arg += "(%s)"%'|'.join(opt.choices) + arg += '(%s)'%'|'.join(opt.choices) elif set(file_map).intersection(set(opt._long_opts)): k = tuple(set(file_map).intersection(set(opt._long_opts))) exts = file_map[k[0]] @@ -295,9 +295,9 @@ class ZshCompleter: # {{{ exts = ('*.%s'%x for x in sorted(exts + [x.upper() for x in exts])) arg += "'_files -g \"%s\"'" % ' '.join(exts) else: - arg += "_files" + arg += '_files' elif (opt.dest in {'pidfile', 'attachment'}): - arg += "_files" + arg += '_files' elif set(opf_opts).intersection(set(opt._long_opts)): arg += "'_files -g \"*.opf\"'" elif set(cover_opts).intersection(set(opt._long_opts)): @@ -408,17 +408,17 @@ class ZshCompleter: # {{{ w('\n_ebook_convert() {') w('\n local iarg oarg context state_descr state line\n typeset -A opt_args\n local ret=1') w("\n _arguments '1: :_ebc_input_args' '*::ebook-convert output:->args' && ret=0") - w("\n case $state in \n (args)") + w('\n case $state in \n (args)') w('\n iarg=${line[1]##*.}; ') w("\n _arguments '1: :_ebc_output_args' '*::ebook-convert options:->args' && ret=0") - w("\n case $state in \n (args)") + w('\n case $state in \n (args)') w('\n oarg=${line[1]##*.}') w('\n iarg="_ebc_input_opts_${(L)iarg}"; oarg="_ebc_output_opts_${(L)oarg}"') w('\n _call_function - $iarg; _call_function - $oarg; _ebc_common_opts; ret=0') w('\n ;;\n esac') - w("\n ;;\n esac\n return ret") + w('\n ;;\n esac\n return ret') w('\n}\n') def do_ebook_edit(self, f): @@ -489,7 +489,7 @@ _ebook_edit() {{ w(' "--version:Show version"\n') for command, desc in iteritems(descs): w(' "%s:%s"\n'%( - command, desc.replace(':', '\\:').replace('"', '\''))) + command, desc.replace(':', '\\:').replace('"', "'"))) w(' )\n _describe -t commands "calibredb command" commands \n}\n') subcommands = [] @@ -1031,7 +1031,7 @@ def opts_and_words(name, op, words, takes_files=False): esac } -complete -F _'''%(opts, words) + fname + ' ' + name +"\n\n").encode('utf-8') +complete -F _'''%(opts, words) + fname + ' ' + name +'\n\n').encode('utf-8') pics = ['bmp', 'gif', 'jpeg', 'jpg', 'png'] # keep sorted alphabetically @@ -1052,7 +1052,7 @@ def opts_and_exts(name, op, exts, cover_opts=('--cover',), opf_opts=(), ;; ''' extras = [] - for eopts, eexts in ((cover_opts, "${pics}"), (opf_opts, "'@(opf)'")): + for eopts, eexts in ((cover_opts, '${pics}'), (opf_opts, "'@(opf)'")): for opt in eopts: extras.append(special_exts_template%(opt, eexts)) extras = '\n'.join(extras) @@ -1085,7 +1085,7 @@ def opts_and_exts(name, op, exts, cover_opts=('--cover',), opf_opts=(), } complete -o filenames -F _'''%dict(pics=spics, - opts=opts, extras=extras, exts=exts) + fname + ' ' + name +"\n\n").encode('utf-8') + opts=opts, extras=extras, exts=exts) + fname + ' ' + name +'\n\n').encode('utf-8') VIEWER = '''\ diff --git a/src/calibre/ptempfile.py b/src/calibre/ptempfile.py index 3fb5f87783..f10826adbe 100644 --- a/src/calibre/ptempfile.py +++ b/src/calibre/ptempfile.py @@ -1,9 +1,9 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' -""" +''' Provides platform independent temporary files that persist even after being closed. -""" +''' import atexit import os import tempfile @@ -158,15 +158,15 @@ def _make_dir(suffix, prefix, base): class PersistentTemporaryFile: - """ + ''' A file-like object that is a temporary file that is available even after being closed on all platforms. It is automatically deleted on normal program termination. - """ + ''' _file = None - def __init__(self, suffix="", prefix="", dir=None, mode='w+b'): + def __init__(self, suffix='', prefix='', dir=None, mode='w+b'): if prefix is None: - prefix = "" + prefix = '' if dir is None: dir = base_dir() fd, name = _make_file(suffix, prefix, dir) @@ -233,7 +233,7 @@ class TemporaryDirectory: class TemporaryFile: - def __init__(self, suffix="", prefix="", dir=None, mode='w+b'): + def __init__(self, suffix='', prefix='', dir=None, mode='w+b'): if prefix is None: prefix = '' if suffix is None: @@ -256,7 +256,7 @@ class TemporaryFile: class SpooledTemporaryFile(tempfile.SpooledTemporaryFile): - def __init__(self, max_size=0, suffix="", prefix="", dir=None, mode='w+b', + def __init__(self, max_size=0, suffix='', prefix='', dir=None, mode='w+b', bufsize=-1): if prefix is None: prefix = '' diff --git a/src/calibre/rpdb.py b/src/calibre/rpdb.py index f904c32e51..8fc42c1906 100644 --- a/src/calibre/rpdb.py +++ b/src/calibre/rpdb.py @@ -22,14 +22,14 @@ QUESTION = '\x00\x01\x02' class RemotePdb(pdb.Pdb): - def __init__(self, addr="127.0.0.1", port=4444, skip=None): + def __init__(self, addr='127.0.0.1', port=4444, skip=None): # Open a reusable socket to allow for reloads self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) self.sock.bind((addr, port)) self.sock.listen(1) with suppress(OSError): - print("pdb is running on %s:%d" % (addr, port), file=sys.stderr) + print('pdb is running on %s:%d' % (addr, port), file=sys.stderr) clientsocket, address = self.sock.accept() clientsocket.setblocking(True) self.clientsocket = clientsocket @@ -64,7 +64,7 @@ class RemotePdb(pdb.Pdb): def do_clear(self, arg): if not arg: - ans = self.ask_question("Clear all breaks? [y/n]: ") + ans = self.ask_question('Clear all breaks? [y/n]: ') if ans.strip().lower() in {'y', 'yes'}: self.clear_all_breaks() self.prints('All breaks cleared') @@ -129,7 +129,7 @@ def cli(port=4444): atexit.register(readline.write_history_file, histfile) p = pdb.Pdb() readline.set_completer(p.complete) - readline.parse_and_bind("tab: complete") + readline.parse_and_bind('tab: complete') sock.setblocking(True) with suppress(KeyboardInterrupt): diff --git a/src/calibre/scraper/test_fetch_backend.py b/src/calibre/scraper/test_fetch_backend.py index 0bd209ea93..0bdd09d36a 100644 --- a/src/calibre/scraper/test_fetch_backend.py +++ b/src/calibre/scraper/test_fetch_backend.py @@ -182,7 +182,7 @@ class TestFetchBackend(unittest.TestCase): ans = Handler(self, *a) return ans - with ThreadingHTTPServer(("", 0), create_handler) as httpd: + with ThreadingHTTPServer(('', 0), create_handler) as httpd: self.server = httpd self.port = httpd.server_address[1] self.server_started.set() diff --git a/src/calibre/spell/import_from.py b/src/calibre/spell/import_from.py index ef6fd54895..2e2e5ae9be 100644 --- a/src/calibre/spell/import_from.py +++ b/src/calibre/spell/import_from.py @@ -21,8 +21,8 @@ from calibre.utils.zipfile import ZipFile from polyglot.builtins import iteritems NS_MAP = { - 'oor': "http://openoffice.org/2001/registry", - 'xs': "http://www.w3.org/2001/XMLSchema", + 'oor': 'http://openoffice.org/2001/registry', + 'xs': 'http://www.w3.org/2001/XMLSchema', 'manifest': 'http://openoffice.org/2001/manifest', } diff --git a/src/calibre/srv/ajax.py b/src/calibre/srv/ajax.py index 24c4049c27..3ad57ae0fe 100644 --- a/src/calibre/srv/ajax.py +++ b/src/calibre/srv/ajax.py @@ -34,11 +34,11 @@ def get_pagination(query, num=100, offset=0): try: num = int(query.get('num', num)) except: - raise HTTPNotFound("Invalid num") + raise HTTPNotFound('Invalid num') try: offset = int(query.get('offset', offset)) except: - raise HTTPNotFound("Invalid offset") + raise HTTPNotFound('Invalid offset') return num, offset diff --git a/src/calibre/srv/auth.py b/src/calibre/srv/auth.py index 34b89b56d9..623d40bd67 100644 --- a/src/calibre/srv/auth.py +++ b/src/calibre/srv/auth.py @@ -159,7 +159,7 @@ class DigestAuth: # {{{ return md5_hex(val) def H_A2(self, data): - """Returns the H(A2) string. See :rfc:`2617` section 3.2.2.3.""" + '''Returns the H(A2) string. See :rfc:`2617` section 3.2.2.3.''' # RFC 2617 3.2.2.3 # If the "qop" directive's value is "auth" or is unspecified, # then A2 is: @@ -167,8 +167,8 @@ class DigestAuth: # {{{ # # If the "qop" value is "auth-int", then A2 is: # A2 = method ":" digest-uri-value ":" H(entity-body) - if self.qop == "auth-int": - a2 = f"{data.method}:{self.uri}:{self.H(data.peek())}" + if self.qop == 'auth-int': + a2 = f'{data.method}:{self.uri}:{self.H(data.peek())}' else: a2 = f'{data.method}:{self.uri}' return self.H(a2) @@ -178,10 +178,10 @@ class DigestAuth: # {{{ ha2 = self.H_A2(data) # Request-Digest -- RFC 2617 3.2.2.1 if self.qop: - req = "{}:{}:{}:{}:{}".format( + req = '{}:{}:{}:{}:{}'.format( self.nonce, self.nonce_count, self.cnonce, self.qop, ha2) else: - req = f"{self.nonce}:{ha2}" + req = f'{self.nonce}:{ha2}' # RFC 2617 3.2.2.2 # diff --git a/src/calibre/srv/http_request.py b/src/calibre/srv/http_request.py index 29ef335f97..be01a891aa 100644 --- a/src/calibre/srv/http_request.py +++ b/src/calibre/srv/http_request.py @@ -68,9 +68,9 @@ def parse_request_uri(uri): def parse_uri(uri, parse_query=True, unquote_func=unquote): scheme, authority, path = parse_request_uri(uri) if path is None: - raise HTTPSimpleResponse(http_client.BAD_REQUEST, "No path component") + raise HTTPSimpleResponse(http_client.BAD_REQUEST, 'No path component') if b'#' in path: - raise HTTPSimpleResponse(http_client.BAD_REQUEST, "Illegal #fragment in Request-URI.") + raise HTTPSimpleResponse(http_client.BAD_REQUEST, 'Illegal #fragment in Request-URI.') if scheme: try: @@ -269,10 +269,10 @@ class HTTPRequest(Connection): rp = int(req_protocol[5]), int(req_protocol[7]) self.method = method.decode('ascii').upper() except Exception: - return self.simple_response(http_client.BAD_REQUEST, "Malformed Request-Line") + return self.simple_response(http_client.BAD_REQUEST, 'Malformed Request-Line') if self.method not in HTTP_METHODS: - return self.simple_response(http_client.BAD_REQUEST, "Unknown HTTP method") + return self.simple_response(http_client.BAD_REQUEST, 'Unknown HTTP method') try: self.request_protocol = protocol_map[rp] @@ -311,36 +311,36 @@ class HTTPRequest(Connection): request_content_length = int(inheaders.get('Content-Length', 0)) if request_content_length > self.max_request_body_size: return self.simple_response(http_client.REQUEST_ENTITY_TOO_LARGE, - "The entity sent with the request exceeds the maximum " - "allowed bytes (%d)." % self.max_request_body_size) + 'The entity sent with the request exceeds the maximum ' + 'allowed bytes (%d).' % self.max_request_body_size) # Persistent connection support if self.response_protocol is HTTP11: # Both server and client are HTTP/1.1 - if inheaders.get("Connection", "") == "close": + if inheaders.get('Connection', '') == 'close': self.close_after_response = True else: # Either the server or client (or both) are HTTP/1.0 - if inheaders.get("Connection", "") != "Keep-Alive": + if inheaders.get('Connection', '') != 'Keep-Alive': self.close_after_response = True # Transfer-Encoding support te = () if self.response_protocol is HTTP11: - rte = inheaders.get("Transfer-Encoding") + rte = inheaders.get('Transfer-Encoding') if rte: - te = [x.strip().lower() for x in rte.split(",") if x.strip()] + te = [x.strip().lower() for x in rte.split(',') if x.strip()] chunked_read = False if te: for enc in te: - if enc == "chunked": + if enc == 'chunked': chunked_read = True else: # Note that, even if we see "chunked", we must reject # if there is an extension we don't recognize. - return self.simple_response(http_client.NOT_IMPLEMENTED, "Unknown transfer encoding: %r" % enc) + return self.simple_response(http_client.NOT_IMPLEMENTED, 'Unknown transfer encoding: %r' % enc) - if inheaders.get("Expect", '').lower() == "100-continue": - buf = BytesIO((HTTP11 + " 100 Continue\r\n\r\n").encode('ascii')) + if inheaders.get('Expect', '').lower() == '100-continue': + buf = BytesIO((HTTP11 + ' 100 Continue\r\n\r\n').encode('ascii')) return self.set_state(WRITE, self.write_continue, buf, inheaders, request_content_length, chunked_read) self.forwarded_for = inheaders.get('X-Forwarded-For') diff --git a/src/calibre/srv/http_response.py b/src/calibre/srv/http_response.py index e151c82999..290e5f06cb 100644 --- a/src/calibre/srv/http_response.py +++ b/src/calibre/srv/http_response.py @@ -118,14 +118,14 @@ def get_ranges(headervalue, content_length): # {{{ result = [] try: - bytesunit, byteranges = headervalue.split("=", 1) + bytesunit, byteranges = headervalue.split('=', 1) except Exception: return None if bytesunit.strip() != 'bytes': return None - for brange in byteranges.split(","): - start, stop = (x.strip() for x in brange.split("-", 1)) + for brange in byteranges.split(','): + start, stop = (x.strip() for x in brange.split('-', 1)) if start: if not stop: stop = content_length - 1 @@ -170,7 +170,7 @@ def gzip_prefix(): def compress_readable_output(src_file, compress_level=6): - crc = zlib.crc32(b"") + crc = zlib.crc32(b'') size = 0 zobj = zlib.compressobj(compress_level, zlib.DEFLATED, -zlib.MAX_WBITS, @@ -187,7 +187,7 @@ def compress_readable_output(src_file, compress_level=6): prefix_written = True data = gzip_prefix() + data yield data - yield zobj.flush() + struct.pack(b"<L", crc & 0xffffffff) + struct.pack(b"<L", size) + yield zobj.flush() + struct.pack(b'<L', crc & 0xffffffff) + struct.pack(b'<L', size) # }}} @@ -419,12 +419,12 @@ class HTTPConnection(HTTPRequest): ct = 'http' if self.method == 'TRACE' else 'plain' buf = [ '%s %d %s' % (self.response_protocol, status_code, http_client.responses[status_code]), - "Content-Length: %s" % len(msg), - "Content-Type: text/%s; charset=UTF-8" % ct, - "Date: " + http_date(), + 'Content-Length: %s' % len(msg), + 'Content-Type: text/%s; charset=UTF-8' % ct, + 'Date: ' + http_date(), ] if self.close_after_response and self.response_protocol is HTTP11: - buf.append("Connection: close") + buf.append('Connection: close') if extra_headers is not None: for h, v in iteritems(extra_headers): buf.append(f'{h}: {v}') @@ -460,8 +460,8 @@ class HTTPConnection(HTTPRequest): self.response_protocol, http_client.REQUESTED_RANGE_NOT_SATISFIABLE, http_client.responses[http_client.REQUESTED_RANGE_NOT_SATISFIABLE]), - "Date: " + http_date(), - "Content-Range: bytes */%d" % content_length, + 'Date: ' + http_date(), + 'Content-Range: bytes */%d' % content_length, ] response_data = header_list_to_file(buf) self.log_access(status_code=http_client.REQUESTED_RANGE_NOT_SATISFIABLE, response_size=response_data.sz) @@ -470,8 +470,8 @@ class HTTPConnection(HTTPRequest): def send_not_modified(self, etag=None): buf = [ '%s %d %s' % (self.response_protocol, http_client.NOT_MODIFIED, http_client.responses[http_client.NOT_MODIFIED]), - "Content-Length: 0", - "Date: " + http_date(), + 'Content-Length: 0', + 'Date: ' + http_date(), ] if etag is not None: buf.append('ETag: ' + etag) diff --git a/src/calibre/srv/legacy.py b/src/calibre/srv/legacy.py index 986a25470c..744317a8dc 100644 --- a/src/calibre/srv/legacy.py +++ b/src/calibre/srv/legacy.py @@ -201,18 +201,18 @@ def build_index(rd, books, num, search, sort, order, start, total, url_base, fie body.append(E.div( E.a(_('Switch to the full interface (non-mobile interface)'), href=ctx.url_for(None), - style="text-decoration: none; color: blue", + style='text-decoration: none; color: blue', title=_('The full interface gives you many more features, ' 'but it may not work well on a small screen')), - style="text-align:center") + style='text-align:center') ) return E.html( E.head( E.title(__appname__ + ' Library'), E.link(rel='icon', href=ctx.url_for('/favicon.png'), type='image/png'), E.link(rel='stylesheet', type='text/css', href=ctx.url_for('/static', what='mobile.css')), - E.link(rel='apple-touch-icon', href=ctx.url_for("/static", what='calibre.png')), - E.meta(name="robots", content="noindex") + E.link(rel='apple-touch-icon', href=ctx.url_for('/static', what='calibre.png')), + E.meta(name='robots', content='noindex') ), # End head body ) # End html diff --git a/src/calibre/srv/loop.py b/src/calibre/srv/loop.py index 0563b27208..c0ea6539dd 100644 --- a/src/calibre/srv/loop.py +++ b/src/calibre/srv/loop.py @@ -42,7 +42,7 @@ from polyglot.queue import Empty, Full READ, WRITE, RDWR, WAIT = 'READ', 'WRITE', 'RDWR', 'WAIT' WAKEUP, JOB_DONE = b'\0', b'\x01' -IPPROTO_IPV6 = getattr(socket, "IPPROTO_IPV6", 41) +IPPROTO_IPV6 = getattr(socket, 'IPPROTO_IPV6', 41) class ReadBuffer: # {{{ @@ -465,7 +465,7 @@ class ServerLoop: self.control_out.close() def __str__(self): - return f"{self.__class__.__name__}({self.bind_address!r})" + return f'{self.__class__.__name__}({self.bind_address!r})' __repr__ = __str__ @property @@ -482,19 +482,19 @@ class ServerLoop: except socket.gaierror: if ':' in host: info = [(socket.AF_INET6, socket.SOCK_STREAM, - 0, "", self.bind_address + (0, 0))] + 0, '', self.bind_address + (0, 0))] else: info = [(socket.AF_INET, socket.SOCK_STREAM, - 0, "", self.bind_address)] + 0, '', self.bind_address)] self.socket = None - msg = "No socket could be created" + msg = 'No socket could be created' for res in info: af, socktype, proto, canonname, sa = res try: self.bind(af, socktype, proto) except OSError as serr: - msg = f"{msg} -- ({sa}: {as_unicode(serr)})" + msg = f'{msg} -- ({sa}: {as_unicode(serr)})' if self.socket: self.socket.close() self.socket = None @@ -556,7 +556,7 @@ class ServerLoop: self.shutdown() def serve_forever(self): - """ Listen for incoming connections. """ + ''' Listen for incoming connections. ''' self.initialize_socket() self.serve() diff --git a/src/calibre/srv/metadata.py b/src/calibre/srv/metadata.py index bbc3260106..d6289987d0 100644 --- a/src/calibre/srv/metadata.py +++ b/src/calibre/srv/metadata.py @@ -30,7 +30,7 @@ IGNORED_FIELDS = frozenset('cover ondevice path marked au_map'.split()) def encode_datetime(dateval): if dateval is None: - return "None" + return 'None' if not isinstance(dateval, datetime): dateval = datetime.combine(dateval, time()) if hasattr(dateval, 'tzinfo') and dateval.tzinfo is None: diff --git a/src/calibre/srv/opds.py b/src/calibre/srv/opds.py index 93ec02c297..295780f78d 100644 --- a/src/calibre/srv/opds.py +++ b/src/calibre/srv/opds.py @@ -121,7 +121,7 @@ def html_to_lxml(raw): raw = '<div>%s</div>'%raw root = parse(raw, keep_doctype=False, namespace_elements=False, maybe_xhtml=False, sanitize_names=True) root = next(root.iterdescendants('div')) - root.set('xmlns', "http://www.w3.org/1999/xhtml") + root.set('xmlns', 'http://www.w3.org/1999/xhtml') raw = etree.tostring(root, encoding='unicode') try: return safe_xml_fromstring(raw, recover=False) @@ -227,16 +227,16 @@ def ACQUISITION_ENTRY(book_id, updated, request_context): fmt = fmt.lower() mt = guess_type('a.'+fmt)[0] if mt: - link = E.link(type=mt, href=get(what=fmt), rel="http://opds-spec.org/acquisition") + link = E.link(type=mt, href=get(what=fmt), rel='http://opds-spec.org/acquisition') ffm = fm.get(fmt.upper()) if ffm: link.set('length', str(ffm['size'])) link.set('mtime', ffm['mtime'].isoformat()) ans.append(link) - ans.append(E.link(type='image/jpeg', href=get(what='cover'), rel="http://opds-spec.org/cover")) - ans.append(E.link(type='image/jpeg', href=get(what='thumb'), rel="http://opds-spec.org/thumbnail")) - ans.append(E.link(type='image/jpeg', href=get(what='cover'), rel="http://opds-spec.org/image")) - ans.append(E.link(type='image/jpeg', href=get(what='thumb'), rel="http://opds-spec.org/image/thumbnail")) + ans.append(E.link(type='image/jpeg', href=get(what='cover'), rel='http://opds-spec.org/cover')) + ans.append(E.link(type='image/jpeg', href=get(what='thumb'), rel='http://opds-spec.org/thumbnail')) + ans.append(E.link(type='image/jpeg', href=get(what='cover'), rel='http://opds-spec.org/image')) + ans.append(E.link(type='image/jpeg', href=get(what='thumb'), rel='http://opds-spec.org/image/thumbnail')) return ans diff --git a/src/calibre/srv/pre_activated.py b/src/calibre/srv/pre_activated.py index e0b4ae8b9c..470da5904f 100644 --- a/src/calibre/srv/pre_activated.py +++ b/src/calibre/srv/pre_activated.py @@ -22,10 +22,10 @@ if islinux: import ctypes class SOCKADDR_NL(ctypes.Structure): - _fields_ = [("nl_family", ctypes.c_ushort), - ("nl_pad", ctypes.c_ushort), - ("nl_pid", ctypes.c_int), - ("nl_groups", ctypes.c_int)] + _fields_ = [('nl_family', ctypes.c_ushort), + ('nl_pad', ctypes.c_ushort), + ('nl_pid', ctypes.c_int), + ('nl_groups', ctypes.c_int)] def getsockfamily(fd): addr = SOCKADDR_NL(0, 0, 0, 0) diff --git a/src/calibre/srv/routes.py b/src/calibre/srv/routes.py index c07d37b9bf..f2b64ea4e3 100644 --- a/src/calibre/srv/routes.py +++ b/src/calibre/srv/routes.py @@ -166,7 +166,7 @@ class Route: if len(self.names) + 2 != len(argspec.args) - len(argspec.defaults or ()): raise route_error('Function must take %d non-default arguments' % (len(self.names) + 2)) if argspec.args[2:len(self.names)+2] != self.names: - raise route_error('Function\'s argument names do not match the variable names in the route') + raise route_error("Function's argument names do not match the variable names in the route") if not frozenset(self.type_checkers).issubset(frozenset(self.names)): raise route_error('There exist type checkers that do not correspond to route variables: %r' % (set(self.type_checkers) - set(self.names))) self.min_size = found_optional_part if found_optional_part is not False else len(matchers) diff --git a/src/calibre/srv/standalone.py b/src/calibre/srv/standalone.py index 45276a555d..69c78b44cf 100644 --- a/src/calibre/srv/standalone.py +++ b/src/calibre/srv/standalone.py @@ -36,7 +36,7 @@ def daemonize(): # {{{ raise SystemExit('fork #1 failed: %s' % as_unicode(e)) # decouple from parent environment - os.chdir("/") + os.chdir('/') os.setsid() os.umask(0) diff --git a/src/calibre/srv/tests/content.py b/src/calibre/srv/tests/content.py index a045a34009..2431bb4c71 100644 --- a/src/calibre/srv/tests/content.py +++ b/src/calibre/srv/tests/content.py @@ -256,16 +256,16 @@ class ContentTest(LibraryBaseTest): bc = data['tree']['c'][1]['c'] self.ae(bc, body_children) - t('<p>a<!--c-->t</p>l', [{"n":"p","x":"a","l":"l","c":[{"s":"c","x":"c","l":"t"}]}]) - t('<p class="foo" id="bar">a', [{"n":"p","x":"a","a":[['class','foo'],['id','bar']]}]) + t('<p>a<!--c-->t</p>l', [{'n':'p','x':'a','l':'l','c':[{'s':'c','x':'c','l':'t'}]}]) + t('<p class="foo" id="bar">a', [{'n':'p','x':'a','a':[['class','foo'],['id','bar']]}]) t( '<svg xlink:href="h"></svg>', [{'n': 'svg', 's': 1, 'a': [['href', 'h', 2]]}], ('http://www.w3.org/1999/xhtml', 'http://www.w3.org/2000/svg', 'http://www.w3.org/1999/xlink') ) text = '🐈\n\t\\mūs"' - t(f"<p id='{text}'>Peña", [{"n":"p","x":"Peña","a":[['id',text]]}]) + t(f"<p id='{text}'>Peña", [{'n':'p','x':'Peña','a':[['id',text]]}]) text = 'a' * (127 * 1024) - t('<p>{0}<p>{0}'.format(text), [{"n":"p","x":text}, {'n':'p','x':text}]) + t('<p>{0}<p>{0}'.format(text), [{'n':'p','x':text}, {'n':'p','x':text}]) # }}} def test_last_read_cache(self): # {{{ diff --git a/src/calibre/srv/tests/http.py b/src/calibre/srv/tests/http.py index 5f217d5806..bc29432571 100644 --- a/src/calibre/srv/tests/http.py +++ b/src/calibre/srv/tests/http.py @@ -344,7 +344,7 @@ class TestHTTP(BaseTest): def edfunc(): num_calls[0] += 1 return b'data' - server.change_handler(lambda conn:conn.etagged_dynamic_response("xxx", edfunc)) + server.change_handler(lambda conn:conn.etagged_dynamic_response('xxx', edfunc)) conn = server.connect() conn.request('GET', '/an_etagged_path') r = conn.getresponse() diff --git a/src/calibre/srv/tests/web_sockets.py b/src/calibre/srv/tests/web_sockets.py index b470c1e30f..0b5fcb998c 100644 --- a/src/calibre/srv/tests/web_sockets.py +++ b/src/calibre/srv/tests/web_sockets.py @@ -234,12 +234,12 @@ class WebSocketTest(BaseTest): with WSTestServer(EchoHandler) as server: simple_test = partial(self.simple_test, server) - for q in ('', '*' * 125, '*' * 126, '*' * 127, '*' * 128, '*' * 65535, '*' * 65536, "Hello-µ@ßöäüàá-UTF-8!!"): + for q in ('', '*' * 125, '*' * 126, '*' * 127, '*' * 128, '*' * 65535, '*' * 65536, 'Hello-µ@ßöäüàá-UTF-8!!'): simple_test([q], [q]) for q in (b'', b'\xfe' * 125, b'\xfe' * 126, b'\xfe' * 127, b'\xfe' * 128, b'\xfe' * 65535, b'\xfe' * 65536): simple_test([q], [q]) - for payload in [b'', b'ping', b'\x00\xff\xfe\xfd\xfc\xfb\x00\xff', b"\xfe" * 125]: + for payload in [b'', b'ping', b'\x00\xff\xfe\xfd\xfc\xfb\x00\xff', b'\xfe' * 125]: simple_test([(PING, payload)], [(PONG, payload)]) simple_test([(PING, 'a'*126)], close_code=PROTOCOL_ERROR, send_close=False) @@ -312,7 +312,7 @@ class WebSocketTest(BaseTest): simple_test([ {'opcode':TEXT, 'fin':0}, {'opcode':CONTINUATION, 'fin':0, 'payload':'x'}, {'opcode':CONTINUATION},], ['x']) - for q in (b'\xc2\xb5', b'\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5', "Hello-µ@ßöäüàá-UTF-8!!".encode()): + for q in (b'\xc2\xb5', b'\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5', 'Hello-µ@ßöäüàá-UTF-8!!'.encode()): frags = [] for i in range(len(q)): b = q[i:i+1] diff --git a/src/calibre/srv/utils.py b/src/calibre/srv/utils.py index 2925e7fa30..36be98ecfb 100644 --- a/src/calibre/srv/utils.py +++ b/src/calibre/srv/utils.py @@ -117,20 +117,20 @@ def error_codes(*errnames): return ans -socket_errors_eintr = error_codes("EINTR", "WSAEINTR") +socket_errors_eintr = error_codes('EINTR', 'WSAEINTR') socket_errors_socket_closed = error_codes( # errors indicating a disconnected connection - "EPIPE", - "EBADF", "WSAEBADF", - "ENOTSOCK", "WSAENOTSOCK", - "ENOTCONN", "WSAENOTCONN", - "ESHUTDOWN", "WSAESHUTDOWN", - "ETIMEDOUT", "WSAETIMEDOUT", - "ECONNREFUSED", "WSAECONNREFUSED", - "ECONNRESET", "WSAECONNRESET", - "ECONNABORTED", "WSAECONNABORTED", - "ENETRESET", "WSAENETRESET", - "EHOSTDOWN", "EHOSTUNREACH", + 'EPIPE', + 'EBADF', 'WSAEBADF', + 'ENOTSOCK', 'WSAENOTSOCK', + 'ENOTCONN', 'WSAENOTCONN', + 'ESHUTDOWN', 'WSAESHUTDOWN', + 'ETIMEDOUT', 'WSAETIMEDOUT', + 'ECONNREFUSED', 'WSAECONNREFUSED', + 'ECONNRESET', 'WSAECONNRESET', + 'ECONNABORTED', 'WSAECONNABORTED', + 'ENETRESET', 'WSAENETRESET', + 'EHOSTDOWN', 'EHOSTUNREACH', ) socket_errors_nonblocking = error_codes( 'EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK') @@ -154,14 +154,14 @@ def create_sock_pair(): def parse_http_list(header_val): - """Parse lists as described by RFC 2068 Section 2. + '''Parse lists as described by RFC 2068 Section 2. In particular, parse comma-separated lists where the elements of the list may include quoted-strings. A quoted-string could contain a comma. A non-quoted string could have quotes in the middle. Neither commas nor quotes count if they are escaped. Only double-quotes count, not single-quotes. - """ + ''' if isinstance(header_val, bytes): slash, dquote, comma = b'\\",' empty = b'' diff --git a/src/calibre/srv/web_socket.py b/src/calibre/srv/web_socket.py index 4ba685b290..50163aec67 100644 --- a/src/calibre/srv/web_socket.py +++ b/src/calibre/srv/web_socket.py @@ -23,10 +23,10 @@ from polyglot.binary import as_base64_unicode from polyglot.queue import Empty, Queue HANDSHAKE_STR = ( - "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: WebSocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: %s\r\n\r\n" + 'HTTP/1.1 101 Switching Protocols\r\n' + 'Upgrade: WebSocket\r\n' + 'Connection: Upgrade\r\n' + 'Sec-WebSocket-Accept: %s\r\n\r\n' ) GUID_STR = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' diff --git a/src/calibre/test_build.py b/src/calibre/test_build.py index 082310ca7a..e403522c37 100644 --- a/src/calibre/test_build.py +++ b/src/calibre/test_build.py @@ -161,7 +161,7 @@ class BuildTest(unittest.TestCase): root = etree.fromstring(raw, parser=etree.XMLParser(recover=True, no_network=True, resolve_entities=False)) self.assertEqual(etree.tostring(root), raw) from lxml import html - html.fromstring("<p>\U0001f63a") + html.fromstring('<p>\U0001f63a') def test_certgen(self): from calibre.utils.certgen import create_key_pair @@ -303,7 +303,7 @@ class BuildTest(unittest.TestCase): os.rmdir(dpath) del h shutil.rmtree(tdir) - m = winutil.create_mutex("test-mutex", False) + m = winutil.create_mutex('test-mutex', False) self.assertRaises(OSError, winutil.create_mutex, 'test-mutex', False) m.close() self.assertEqual(winutil.parse_cmdline('"c:\\test exe.exe" "some arg" 2'), ('c:\\test exe.exe', 'some arg', '2')) @@ -422,7 +422,7 @@ class BuildTest(unittest.TestCase): def test_pykakasi(self): from calibre.ebooks.unihandecode.jadecoder import Jadecoder - self.assertEqual(Jadecoder().decode("自転車生活の愉しみ"), 'Jitensha Seikatsu no Tanoshi mi') + self.assertEqual(Jadecoder().decode('自転車生活の愉しみ'), 'Jitensha Seikatsu no Tanoshi mi') def test_imaging(self): from PIL import Image diff --git a/src/calibre/translations/msgfmt.py b/src/calibre/translations/msgfmt.py index 225d47ef85..eaa1fd86d0 100644 --- a/src/calibre/translations/msgfmt.py +++ b/src/calibre/translations/msgfmt.py @@ -2,7 +2,7 @@ # Written by Martin v. Löwis <loewis@informatik.hu-berlin.de> -"""Generate binary message catalog from textual translation description. +'''Generate binary message catalog from textual translation description. This program converts a textual Uniforum-style message catalog (.po file) into a binary GNU catalog (.mo file). This is essentially the same function as the @@ -24,7 +24,7 @@ Options: -V --version Display version information and exit. -""" +''' import array import ast @@ -34,7 +34,7 @@ import struct import sys from email.parser import HeaderParser -__version__ = "1.2" +__version__ = '1.2' MESSAGES = {} STATS = {'translated': 0, 'untranslated': 0, 'uniqified': 0} @@ -50,7 +50,7 @@ def usage(code, msg=''): def add(ctxt, msgid, msgstr, fuzzy): - "Add a non-fuzzy translation to the dictionary." + 'Add a non-fuzzy translation to the dictionary.' if (not fuzzy or not msgid) and msgstr: if msgid: STATS['translated'] += 1 @@ -63,14 +63,14 @@ def add(ctxt, msgid, msgstr, fuzzy): NON_UNIQUE.add(msgstr) MESSAGES[msgid] = msgstr else: - MESSAGES[b"%b\x04%b" % (ctxt, msgid)] = msgstr + MESSAGES[b'%b\x04%b' % (ctxt, msgid)] = msgstr else: if msgid: STATS['untranslated'] += 1 def generate(): - "Return the generated output." + 'Return the generated output.' # the keys are sorted in the .mo file keys = sorted(MESSAGES.keys()) offsets = [] @@ -96,7 +96,7 @@ def generate(): koffsets += [l1, o1+keystart] voffsets += [l2, o2+valuestart] offsets = koffsets + voffsets - output = struct.pack("Iiiiiii", + output = struct.pack('Iiiiiii', 0x950412de, # Magic 0, # Version len(keys), # # of entries @@ -104,9 +104,9 @@ def generate(): 7*4+len(keys)*8, # start of value index 0, 0) # size and offset of hash table try: - output += array.array("i", offsets).tobytes() + output += array.array('i', offsets).tobytes() except AttributeError: - output += array.array("i", offsets).tostring() + output += array.array('i', offsets).tostring() output += ids output += strs return output @@ -235,7 +235,7 @@ def make(filename, outfile): if hasattr(outfile, 'write'): outfile.write(output) else: - with open(outfile, "wb") as f: + with open(outfile, 'wb') as f: f.write(output) except OSError as msg: print(msg, file=sys.stderr) @@ -277,7 +277,7 @@ def main(): if opt in ('-h', '--help'): usage(0) elif opt in ('-V', '--version'): - print("msgfmt.py", __version__, file=sys.stderr) + print('msgfmt.py', __version__, file=sys.stderr) sys.exit(0) elif opt in ('-o', '--output-file'): outfile = arg diff --git a/src/calibre/utils/bibtex.py b/src/calibre/utils/bibtex.py index 77c8ea573b..2f0af82271 100644 --- a/src/calibre/utils/bibtex.py +++ b/src/calibre/utils/bibtex.py @@ -2559,15 +2559,15 @@ class BibTeX: self.escape = re.compile('[#&%_]') def ValidateCitationKey(self, text): - """ + ''' Removes characters not allowed in BibTeX keys - """ + ''' return self.invalid_cit.sub('', text) def braceUppercase(self, text): - """ + ''' Convert uppercase letters to bibtex encoded uppercase - """ + ''' return self.upper.sub(lambda m: '{%s}' % m.group(), text) def resolveEntities(self, text): @@ -2579,18 +2579,18 @@ class BibTeX: return text.replace('$}{$', '') def escapeSpecialCharacters(self, text): - """ + ''' Latex escaping some (not all) special characters - """ + ''' text = text.replace('\\', '\\\\') text = text.replace('~', '{\\char`\\~}') # TILDE return self.escape.sub(lambda m: '\\%s' % m.group(), text) # Calibre functions: Option to go to official ASCII Bibtex or unofficial UTF-8 def utf8ToBibtex(self, text): - """ + ''' Go from an unicode entry to ASCII Bibtex format without encoding - """ + ''' if len(text) == 0: return '' text = self.resolveEntities(text) @@ -2600,15 +2600,15 @@ class BibTeX: return text def bibtex_author_format(self, item): - """ + ''' Format authors for Bibtex compliance (get a list as input) - """ + ''' return self.utf8ToBibtex(' and '.join([author for author in item])) def stripUnmatchedSyntax(self, text, open_character, close_character): - """ + ''' Strips unmatched BibTeX syntax - """ + ''' stack = [] assert len(open_character) == 1 and len(close_character) == 1 remove = [] diff --git a/src/calibre/utils/certgen.py b/src/calibre/utils/certgen.py index e654b645d4..c2c82457f6 100644 --- a/src/calibre/utils/certgen.py +++ b/src/calibre/utils/certgen.py @@ -99,7 +99,7 @@ def create_server_cert( def develop(): cacert, cakey, cert, pkey = create_server_cert('test.me', alt_names=['DNS:moose.cat', 'DNS:huge.bat']) - print("CA Certificate") + print('CA Certificate') print(cert_info(cacert)) print(), print(), print() print('Server Certificate') diff --git a/src/calibre/utils/cleantext.py b/src/calibre/utils/cleantext.py index ccf4ffaac7..394ca9eeba 100644 --- a/src/calibre/utils/cleantext.py +++ b/src/calibre/utils/cleantext.py @@ -76,10 +76,10 @@ def test_clean_xml_chars(): def unescape(text, rm=False, rchar=''): def fixup(m, rm=rm, rchar=rchar): text = m.group(0) - if text[:2] == "&#": + if text[:2] == '&#': # character reference try: - if text[:3] == "&#x": + if text[:3] == '&#x': return codepoint_to_chr(int(text[3:-1], 16)) else: return codepoint_to_chr(int(text[2:-1])) @@ -94,4 +94,4 @@ def unescape(text, rm=False, rchar=''): if rm: return rchar # replace by char return text # leave as is - return re.sub("&#?\\w+;", fixup, text) + return re.sub('&#?\\w+;', fixup, text) diff --git a/src/calibre/utils/config.py b/src/calibre/utils/config.py index b306b66513..3f8e379db4 100644 --- a/src/calibre/utils/config.py +++ b/src/calibre/utils/config.py @@ -59,7 +59,7 @@ class CustomHelpFormatter(optparse.IndentedHelpFormatter): def format_heading(self, heading): from calibre.utils.terminal import colored - return "%*s%s:\n" % (self.current_indent, '', + return '%*s%s:\n' % (self.current_indent, '', colored(heading, fg='blue', bold=True)) def format_option(self, option): @@ -71,11 +71,11 @@ class CustomHelpFormatter(optparse.IndentedHelpFormatter): opts = self.option_strings[option] opt_width = self.help_position - self.current_indent - 2 if len(opts) > opt_width: - opts = "%*s%s\n" % (self.current_indent, "", + opts = '%*s%s\n' % (self.current_indent, '', colored(opts, fg='green')) indent_first = self.help_position else: # start help on same line as opts - opts = "%*s%-*s " % (self.current_indent, "", opt_width + + opts = '%*s%-*s ' % (self.current_indent, '', opt_width + len(colored('', fg='green')), colored(opts, fg='green')) indent_first = 0 result.append(opts) @@ -85,12 +85,12 @@ class CustomHelpFormatter(optparse.IndentedHelpFormatter): for line in help_text: help_lines.extend(textwrap.wrap(line, self.help_width)) - result.append("%*s%s\n" % (indent_first, "", help_lines[0])) - result.extend(["%*s%s\n" % (self.help_position, "", line) + result.append('%*s%s\n' % (indent_first, '', help_lines[0])) + result.extend(['%*s%s\n' % (self.help_position, '', line) for line in help_lines[1:]]) - elif opts[-1] != "\n": - result.append("\n") - return "".join(result)+'\n' + elif opts[-1] != '\n': + result.append('\n') + return ''.join(result)+'\n' class OptionParser(optparse.OptionParser): @@ -111,7 +111,7 @@ class OptionParser(optparse.OptionParser): epilog = _('Created by ')+colored(__author__, fg='cyan') usage += '\n\n'+_('''Whenever you pass arguments to %prog that have spaces in them, ''' '''enclose the arguments in quotation marks. For example: "{}"''').format( - "C:\\some path with spaces" if iswindows else '/some path/with spaces') +'\n' + 'C:\\some path with spaces' if iswindows else '/some path/with spaces') +'\n' if version is None: version = '%%prog (%s %s)'%(__appname__, get_version()) optparse.OptionParser.__init__(self, usage=usage, version=version, epilog=epilog, @@ -120,8 +120,8 @@ class OptionParser(optparse.OptionParser): self.gui_mode = gui_mode if False: # Translatable string from optparse - _("Options") - _("show this help message and exit") + _('Options') + _('show this help message and exit') _("show program's version number and exit") def print_usage(self, file=None): diff --git a/src/calibre/utils/config_base.py b/src/calibre/utils/config_base.py index bc06642655..cfb9101159 100644 --- a/src/calibre/utils/config_base.py +++ b/src/calibre/utils/config_base.py @@ -415,7 +415,7 @@ class Config(ConfigInterface): try: src = src_bytes.decode('utf-8') except ValueError: - print("Failed to parse", path) + print('Failed to parse', path) traceback.print_exc() if not src: path = path.rpartition('.')[0] @@ -594,7 +594,7 @@ def create_global_prefs(conf_obj=None): 'can cause problems with text that starts with numbers and is ' 'a little slower.')) - c.add_opt('migrated', default=False, help='For Internal use. Don\'t modify.') + c.add_opt('migrated', default=False, help="For Internal use. Don't modify.") return c diff --git a/src/calibre/utils/ffml_processor.py b/src/calibre/utils/ffml_processor.py index 4289b4fc18..711bb58561 100644 --- a/src/calibre/utils/ffml_processor.py +++ b/src/calibre/utils/ffml_processor.py @@ -178,7 +178,7 @@ class UrlNode(Node): class FFMLProcessor: - r""" + r''' This class is parser for the Formatter Function Markup Language (FFML). It provides output methods for RST and HTML. @@ -191,18 +191,18 @@ class FFMLProcessor: - pushing the "FFML documentation" button in the "Formatter function documentation editor". How to access the editor is described in "General information" dialog mentioned above. - """ + ''' # ====== API ====== def print_node_tree(self, node, indent=0): - """ + ''' Pretty print a Formatter Function Markup Language (FFML) parse tree. :param node: The root of the tree you want printed. :param indent: The indent level of the tree. The outermost root should have an indent of zero. - """ + ''' if node.node_kind() in (NodeKinds.TEXT, NodeKinds.CODE_TEXT, NodeKinds.CHARACTER, NodeKinds.CODE_BLOCK, NodeKinds.ITALIC_TEXT, NodeKinds.GUI_LABEL, NodeKinds.BOLD_TEXT): @@ -215,7 +215,7 @@ class FFMLProcessor: self.print_node_tree(n, indent+1) def parse_document(self, doc, name, safe=True): - """ + ''' Given a Formatter Function Markup Language (FFML) document, return a parse tree for that document. @@ -226,7 +226,7 @@ class FFMLProcessor: recover using the Engiish version as well as display an error. :return: a parse tree for the document - """ + ''' def initialize(txt): self.input_line = 1 self.input = txt @@ -273,7 +273,7 @@ class FFMLProcessor: return add_exception_text(DocumentNode(), e, doc) def tree_to_html(self, tree, depth=0): - """ + ''' Given a Formatter Function Markup Language (FFML) parse tree, return a string containing the HTML for that tree. @@ -281,7 +281,7 @@ class FFMLProcessor: :param depth: the recursion level. This is used for debugging. :return: a string containing the HTML text - """ + ''' result = '' if tree.node_kind() == NodeKinds.TEXT: result += tree.escaped_text() @@ -320,7 +320,7 @@ class FFMLProcessor: return result def document_to_html(self, document, name, safe=True): - """ + ''' Given a document in the Formatter Function Markup Language (FFML), return that document in HTML format. @@ -332,7 +332,7 @@ class FFMLProcessor: :return: a string containing the HTML - """ + ''' tree = self.parse_document(document, name, safe=safe) return self.tree_to_html(tree, 0) @@ -362,7 +362,7 @@ class FFMLProcessor: return result def tree_to_rst(self, tree, indent, result=None): - """ + ''' Given a Formatter Function Markup Language (FFML) parse tree, return a string containing the RST (sphinx reStructuredText) for that tree. @@ -372,7 +372,7 @@ class FFMLProcessor: the RST output indented. :return: a string containing the RST text - """ + ''' def indent_text(txt): nonlocal result @@ -428,7 +428,7 @@ class FFMLProcessor: return result def document_to_rst(self, document, name, indent=0, prefix=None, safe=True): - """ + ''' Given a document in the Formatter Function Markup Language (FFML), return that document in RST (sphinx reStructuredText) format. @@ -446,7 +446,7 @@ class FFMLProcessor: :return: a string containing the RST text - """ + ''' doc = self.tree_to_rst(self.parse_document(document, name, safe=safe), indent) if prefix is not None: doc = prefix + doc.lstrip(' ' * indent) diff --git a/src/calibre/utils/fonts/sfnt/__init__.py b/src/calibre/utils/fonts/sfnt/__init__.py index c416da49c2..2aee477c14 100644 --- a/src/calibre/utils/fonts/sfnt/__init__.py +++ b/src/calibre/utils/fonts/sfnt/__init__.py @@ -59,10 +59,10 @@ class FixedProperty: def max_power_of_two(x): - """ + ''' Return the highest exponent of two, so that (2 ** exponent) <= x - """ + ''' exponent = 0 while x: x = x >> 1 diff --git a/src/calibre/utils/fonts/sfnt/cff/constants.py b/src/calibre/utils/fonts/sfnt/cff/constants.py index 4500dfa744..d43bdda9c9 100644 --- a/src/calibre/utils/fonts/sfnt/cff/constants.py +++ b/src/calibre/utils/fonts/sfnt/cff/constants.py @@ -80,102 +80,102 @@ cff_standard_strings = [ STANDARD_CHARSETS = [ # {{{ # ISOAdobe -(".notdef", "space", "exclam", "quotedbl", "numbersign", "dollar", - "percent", "ampersand", "quoteright", "parenleft", "parenright", - "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", - "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", - "colon", "semicolon", "less", "equal", "greater", "question", "at", - "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", - "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", - "bracketleft", "backslash", "bracketright", "asciicircum", - "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", - "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", - "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", - "exclamdown", "cent", "sterling", "fraction", "yen", "florin", - "section", "currency", "quotesingle", "quotedblleft", "guillemotleft", - "guilsinglleft", "guilsinglright", "fi", "fl", "endash", "dagger", - "daggerdbl", "periodcentered", "paragraph", "bullet", "quotesinglbase", - "quotedblbase", "quotedblright", "guillemotright", "ellipsis", - "perthousand", "questiondown", "grave", "acute", "circumflex", "tilde", - "macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", - "hungarumlaut", "ogonek", "caron", "emdash", "AE", "ordfeminine", - "Lslash", "Oslash", "OE", "ordmasculine", "ae", "dotlessi", "lslash", - "oslash", "oe", "germandbls", "onesuperior", "logicalnot", "mu", - "trademark", "Eth", "onehalf", "plusminus", "Thorn", "onequarter", - "divide", "brokenbar", "degree", "thorn", "threequarters", - "twosuperior", "registered", "minus", "eth", "multiply", - "threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", - "Agrave", "Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", - "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", - "Ntilde", "Oacute", "Ocircumflex", "Odieresis", "Ograve", "Otilde", - "Scaron", "Uacute", "Ucircumflex", "Udieresis", "Ugrave", "Yacute", - "Ydieresis", "Zcaron", "aacute", "acircumflex", "adieresis", "agrave", - "aring", "atilde", "ccedilla", "eacute", "ecircumflex", "edieresis", - "egrave", "iacute", "icircumflex", "idieresis", "igrave", "ntilde", - "oacute", "ocircumflex", "odieresis", "ograve", "otilde", "scaron", - "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis", - "zcaron"), +('.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', + 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright', + 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', + 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', + 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', 'at', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'bracketleft', 'backslash', 'bracketright', 'asciicircum', + 'underscore', 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', + 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', + 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin', + 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft', + 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger', + 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase', + 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', + 'perthousand', 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', + 'macron', 'breve', 'dotaccent', 'dieresis', 'ring', 'cedilla', + 'hungarumlaut', 'ogonek', 'caron', 'emdash', 'AE', 'ordfeminine', + 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae', 'dotlessi', 'lslash', + 'oslash', 'oe', 'germandbls', 'onesuperior', 'logicalnot', 'mu', + 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn', 'onequarter', + 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters', + 'twosuperior', 'registered', 'minus', 'eth', 'multiply', + 'threesuperior', 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', + 'Agrave', 'Aring', 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', + 'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', + 'Ntilde', 'Oacute', 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', + 'Scaron', 'Uacute', 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', + 'Ydieresis', 'Zcaron', 'aacute', 'acircumflex', 'adieresis', 'agrave', + 'aring', 'atilde', 'ccedilla', 'eacute', 'ecircumflex', 'edieresis', + 'egrave', 'iacute', 'icircumflex', 'idieresis', 'igrave', 'ntilde', + 'oacute', 'ocircumflex', 'odieresis', 'ograve', 'otilde', 'scaron', + 'uacute', 'ucircumflex', 'udieresis', 'ugrave', 'yacute', 'ydieresis', + 'zcaron'), # Expert -("notdef", "space", "exclamsmall", "Hungarumlautsmall", "dollaroldstyle", - "dollarsuperior", "ampersandsmall", "Acutesmall", "parenleftsuperior", - "parenrightsuperior", "twodotenleader", "onedotenleader", "comma", - "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle", - "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", - "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", - "colon", "semicolon", "commasuperior", "threequartersemdash", - "periodsuperior", "questionsmall", "asuperior", "bsuperior", - "centsuperior", "dsuperior", "esuperior", "isuperior", "lsuperior", - "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior", - "tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior", - "parenrightinferior", "Circumflexsmall", "hyphensuperior", - "Gravesmall", "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", - "Fsmall", "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall", - "Msmall", "Nsmall", "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall", - "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", - "colonmonetary", "onefitted", "rupiah", "Tildesmall", - "exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", - "Zcaronsmall", "Dieresissmall", "Brevesmall", "Caronsmall", - "Dotaccentsmall", "Macronsmall", "figuredash", "hypheninferior", - "Ogoneksmall", "Ringsmall", "Cedillasmall", "onequarter", "onehalf", - "threequarters", "questiondownsmall", "oneeighth", "threeeighths", - "fiveeighths", "seveneighths", "onethird", "twothirds", "zerosuperior", - "onesuperior", "twosuperior", "threesuperior", "foursuperior", - "fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", - "ninesuperior", "zeroinferior", "oneinferior", "twoinferior", - "threeinferior", "fourinferior", "fiveinferior", "sixinferior", - "seveninferior", "eightinferior", "nineinferior", "centinferior", - "dollarinferior", "periodinferior", "commainferior", "Agravesmall", - "Aacutesmall", "Acircumflexsmall", "Atildesmall", "Adieresissmall", - "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall", "Eacutesmall", - "Ecircumflexsmall", "Edieresissmall", "Igravesmall", "Iacutesmall", - "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall", - "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall", - "Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", - "Uacutesmall", "Ucircumflexsmall", "Udieresissmall", "Yacutesmall", - "Thornsmall", "Ydieresissmall"), +('notdef', 'space', 'exclamsmall', 'Hungarumlautsmall', 'dollaroldstyle', + 'dollarsuperior', 'ampersandsmall', 'Acutesmall', 'parenleftsuperior', + 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'comma', + 'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle', + 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', + 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', + 'colon', 'semicolon', 'commasuperior', 'threequartersemdash', + 'periodsuperior', 'questionsmall', 'asuperior', 'bsuperior', + 'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior', + 'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior', + 'tsuperior', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior', + 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', + 'Gravesmall', 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', + 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', + 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', + 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall', 'Ysmall', 'Zsmall', + 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', + 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', + 'Zcaronsmall', 'Dieresissmall', 'Brevesmall', 'Caronsmall', + 'Dotaccentsmall', 'Macronsmall', 'figuredash', 'hypheninferior', + 'Ogoneksmall', 'Ringsmall', 'Cedillasmall', 'onequarter', 'onehalf', + 'threequarters', 'questiondownsmall', 'oneeighth', 'threeeighths', + 'fiveeighths', 'seveneighths', 'onethird', 'twothirds', 'zerosuperior', + 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', + 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', + 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior', + 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', + 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', + 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall', + 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', + 'Aringsmall', 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', + 'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall', + 'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall', + 'Ogravesmall', 'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall', + 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', + 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', + 'Thornsmall', 'Ydieresissmall'), # Expert Subset -(".notdef", "space", "dollaroldstyle", "dollarsuperior", - "parenleftsuperior", "parenrightsuperior", "twodotenleader", - "onedotenleader", "comma", "hyphen", "period", "fraction", - "zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle", - "fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle", - "eightoldstyle", "nineoldstyle", "colon", "semicolon", - "commasuperior", "threequartersemdash", "periodsuperior", - "asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", - "isuperior", "lsuperior", "msuperior", "nsuperior", "osuperior", - "rsuperior", "ssuperior", "tsuperior", "ff", "fi", "fl", "ffi", - "ffl", "parenleftinferior", "parenrightinferior", "hyphensuperior", - "colonmonetary", "onefitted", "rupiah", "centoldstyle", - "figuredash", "hypheninferior", "onequarter", "onehalf", - "threequarters", "oneeighth", "threeeighths", "fiveeighths", - "seveneighths", "onethird", "twothirds", "zerosuperior", - "onesuperior", "twosuperior", "threesuperior", "foursuperior", - "fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", - "ninesuperior", "zeroinferior", "oneinferior", "twoinferior", - "threeinferior", "fourinferior", "fiveinferior", "sixinferior", - "seveninferior", "eightinferior", "nineinferior", "centinferior", - "dollarinferior", "periodinferior", "commainferior"), +('.notdef', 'space', 'dollaroldstyle', 'dollarsuperior', + 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', + 'onedotenleader', 'comma', 'hyphen', 'period', 'fraction', + 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', + 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', + 'eightoldstyle', 'nineoldstyle', 'colon', 'semicolon', + 'commasuperior', 'threequartersemdash', 'periodsuperior', + 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', + 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior', + 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'fi', 'fl', 'ffi', + 'ffl', 'parenleftinferior', 'parenrightinferior', 'hyphensuperior', + 'colonmonetary', 'onefitted', 'rupiah', 'centoldstyle', + 'figuredash', 'hypheninferior', 'onequarter', 'onehalf', + 'threequarters', 'oneeighth', 'threeeighths', 'fiveeighths', + 'seveneighths', 'onethird', 'twothirds', 'zerosuperior', + 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', + 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', + 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior', + 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', + 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', + 'dollarinferior', 'periodinferior', 'commainferior'), ] # }}} diff --git a/src/calibre/utils/fonts/sfnt/cff/dict_data.py b/src/calibre/utils/fonts/sfnt/cff/dict_data.py index 730f6dd50f..930a4929a4 100644 --- a/src/calibre/utils/fonts/sfnt/cff/dict_data.py +++ b/src/calibre/utils/fonts/sfnt/cff/dict_data.py @@ -8,20 +8,20 @@ __docformat__ = 'restructuredtext en' from struct import pack, unpack_from t1_operand_encoding = [None] * 256 -t1_operand_encoding[0:32] = (32) * ["do_operator"] -t1_operand_encoding[32:247] = (247 - 32) * ["read_byte"] -t1_operand_encoding[247:251] = (251 - 247) * ["read_small_int1"] -t1_operand_encoding[251:255] = (255 - 251) * ["read_small_int2"] -t1_operand_encoding[255] = "read_long_int" +t1_operand_encoding[0:32] = (32) * ['do_operator'] +t1_operand_encoding[32:247] = (247 - 32) * ['read_byte'] +t1_operand_encoding[247:251] = (251 - 247) * ['read_small_int1'] +t1_operand_encoding[251:255] = (255 - 251) * ['read_small_int2'] +t1_operand_encoding[255] = 'read_long_int' t2_operand_encoding = t1_operand_encoding[:] -t2_operand_encoding[28] = "read_short_int" -t2_operand_encoding[255] = "read_fixed_1616" +t2_operand_encoding[28] = 'read_short_int' +t2_operand_encoding[255] = 'read_fixed_1616' cff_dict_operand_encoding = t2_operand_encoding[:] -cff_dict_operand_encoding[29] = "read_long_int" -cff_dict_operand_encoding[30] = "read_real_number" -cff_dict_operand_encoding[255] = "reserved" +cff_dict_operand_encoding[29] = 'read_long_int' +cff_dict_operand_encoding[30] = 'read_real_number' +cff_dict_operand_encoding[255] = 'reserved' real_nibbles = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'E', 'E-', None, '-'] @@ -42,15 +42,15 @@ class ByteCode(dict): return -(b0-251)*256 - b1 - 108, index+1 def read_short_int(self, b0, data, index): - value, = unpack_from(b">h", data, index) + value, = unpack_from(b'>h', data, index) return value, index+2 def read_long_int(self, b0, data, index): - value, = unpack_from(b">l", data, index) + value, = unpack_from(b'>l', data, index) return value, index+4 def read_fixed_1616(self, b0, data, index): - value, = unpack_from(b">l", data, index) + value, = unpack_from(b'>l', data, index) return value / 65536.0, index+4 def read_real_number(self, b0, data, index): @@ -70,17 +70,17 @@ class ByteCode(dict): def write_float(self, f, encoding='ignored'): s = str(f).upper() - if s[:2] == "0.": + if s[:2] == '0.': s = s[1:] - elif s[:3] == "-0.": - s = "-" + s[2:] + elif s[:3] == '-0.': + s = '-' + s[2:] nibbles = [] while s: c = s[0] s = s[1:] - if c == "E" and s[:1] == "-": + if c == 'E' and s[:1] == '-': s = s[1:] - c = "E-" + c = 'E-' nibbles.append(real_nibbles_map[c]) nibbles.append(0xf) if len(nibbles) % 2: @@ -90,7 +90,7 @@ class ByteCode(dict): d.append(nibbles[i] << 4 | nibbles[i+1]) return bytes(d) - def write_int(self, value, encoding="cff"): + def write_int(self, value, encoding='cff'): four_byte_op = {'cff':29, 't1':255}.get(encoding, None) if -107 <= value <= 107: @@ -103,15 +103,15 @@ class ByteCode(dict): code = bytes(bytearray([(value >> 8) + 251, (value & 0xFF)])) elif four_byte_op is None: # T2 only supports 2 byte ints - code = bytes(bytearray([28])) + pack(b">h", value) + code = bytes(bytearray([28])) + pack(b'>h', value) else: - code = bytes(bytearray([four_byte_op])) + pack(b">l", value) + code = bytes(bytearray([four_byte_op])) + pack(b'>l', value) return code def write_offset(self, value): - return bytes(bytearray([29])) + pack(b">l", value) + return bytes(bytearray([29])) + pack(b'>l', value) - def write_number(self, value, encoding="cff"): + def write_number(self, value, encoding='cff'): f = self.write_float if isinstance(value, float) else self.write_int return f(value, encoding) diff --git a/src/calibre/utils/fonts/sfnt/cff/table.py b/src/calibre/utils/fonts/sfnt/cff/table.py index 7a63813e6e..dbe57f3a6a 100644 --- a/src/calibre/utils/fonts/sfnt/cff/table.py +++ b/src/calibre/utils/fonts/sfnt/cff/table.py @@ -133,7 +133,7 @@ class Charset(list): super().__init__() self.standard_charset = offset if offset in {0, 1, 2} else None if is_CID and self.standard_charset is not None: - raise ValueError("CID font must not use a standard charset") + raise ValueError('CID font must not use a standard charset') if self.standard_charset is None: self.append(b'.notdef') fmt = unpack_from(b'>B', raw, offset)[0] diff --git a/src/calibre/utils/fonts/sfnt/loca.py b/src/calibre/utils/fonts/sfnt/loca.py index 1f79d8b68a..a354fc3ae9 100644 --- a/src/calibre/utils/fonts/sfnt/loca.py +++ b/src/calibre/utils/fonts/sfnt/loca.py @@ -77,7 +77,7 @@ class LocaTable(UnknownTable): self.fmt = four_byte_type_code() vals = array.array(self.fmt, vals) - if sys.byteorder != "big": + if sys.byteorder != 'big': vals.byteswap() self.raw = vals.tobytes() subset = update diff --git a/src/calibre/utils/formatter.py b/src/calibre/utils/formatter.py index 9e0fdb50d2..bce17a8d38 100644 --- a/src/calibre/utils/formatter.py +++ b/src/calibre/utils/formatter.py @@ -576,8 +576,8 @@ class _Parser: while not self.token_op_is(')'): a = self.top_expr() if a.node_type not in (Node.NODE_ASSIGN, Node.NODE_RVALUE): - self.error(_("Parameters to a function must be " - "variables or assignments")) + self.error(_('Parameters to a function must be ' + 'variables or assignments')) if a.node_type == Node.NODE_RVALUE: a = AssignNode(line_number, a.name, ConstantNode(self.line_number, '')) arguments.append(a) @@ -961,7 +961,7 @@ class FormatterFuncsCaller: return call - e = AttributeError(_("No function named {!r} exists").format(name)) + e = AttributeError(_('No function named {!r} exists').format(name)) e.is_internal = True raise e @@ -1074,20 +1074,20 @@ class _Interpreter: try: start_val = int(self.float_deal_with_none(self.expr(prog.start_expr))) except ValueError: - self.error(_("{0}: {1} must be an integer").format('for', 'start'), line_number) + self.error(_('{0}: {1} must be an integer').format('for', 'start'), line_number) try: stop_val = int(self.float_deal_with_none(self.expr(prog.stop_expr))) except ValueError: - self.error(_("{0}: {1} must be an integer").format('for', 'stop'), line_number) + self.error(_('{0}: {1} must be an integer').format('for', 'stop'), line_number) try: step_val = int(self.float_deal_with_none(self.expr(prog.step_expr))) except ValueError: - self.error(_("{0}: {1} must be an integer").format('for', 'step'), line_number) + self.error(_('{0}: {1} must be an integer').format('for', 'step'), line_number) try: limit_val = (1000 if prog.limit_expr is None else int(self.float_deal_with_none(self.expr(prog.limit_expr)))) except ValueError: - self.error(_("{0}: {1} must be an integer").format('for', 'limit'), line_number) + self.error(_('{0}: {1} must be an integer').format('for', 'limit'), line_number) var = prog.variable if (self.break_reporter): self.break_reporter("'for': start value", str(start_val), line_number) @@ -1099,7 +1099,7 @@ class _Interpreter: range_gen = range(start_val, stop_val, step_val) if len(range_gen) > limit_val: self.error( - _("{0}: the range length ({1}) is larger than the limit ({2})").format( + _('{0}: the range length ({1}) is larger than the limit ({2})').format( 'for', str(len(range_gen)), str(limit_val)), line_number) for x in (str(x) for x in range_gen): try: @@ -1187,8 +1187,8 @@ class _Interpreter: self.break_reporter(prog.node_name, _('before evaluating arguments'), prog.line_number) line_number, argument_list, block = self.local_functions[prog.name].attributes_to_tuple() if len(prog.arguments) > len(argument_list): - self.error(_("Function {0}: argument count mismatch -- " - "{1} given, at most {2} required").format(prog.name, + self.error(_('Function {0}: argument count mismatch -- ' + '{1} given, at most {2} required').format(prog.name, len(prog.arguments), len(argument_list)), prog.line_number) @@ -1330,15 +1330,15 @@ class _Interpreter: for i in range(0, len(prog.expression_list)-1, 2): tst = self.expr(prog.expression_list[i]) if self.break_reporter: - self.break_reporter("switch_if(): test expr", tst, prog.line_number) + self.break_reporter('switch_if(): test expr', tst, prog.line_number) if tst: res = self.expr(prog.expression_list[i+1]) if self.break_reporter: - self.break_reporter("switch_if(): value expr", res, prog.line_number) + self.break_reporter('switch_if(): value expr', res, prog.line_number) return res res = self.expr(prog.expression_list[-1]) if (self.break_reporter): - self.break_reporter("switch_if(): default expr", res, prog.line_number) + self.break_reporter('switch_if(): default expr', res, prog.line_number) return res def do_node_strcat(self, prog): @@ -1387,14 +1387,14 @@ class _Interpreter: return res INFIX_STRING_COMPARE_OPS = { - "==": lambda x, y: strcmp(x, y) == 0, - "!=": lambda x, y: strcmp(x, y) != 0, - "<": lambda x, y: strcmp(x, y) < 0, - "<=": lambda x, y: strcmp(x, y) <= 0, - ">": lambda x, y: strcmp(x, y) > 0, - ">=": lambda x, y: strcmp(x, y) >= 0, - "in": lambda x, y: re.search(x, y, flags=re.I), - "inlist": lambda x, y: list(filter(partial(re.search, x, flags=re.I), + '==': lambda x, y: strcmp(x, y) == 0, + '!=': lambda x, y: strcmp(x, y) != 0, + '<': lambda x, y: strcmp(x, y) < 0, + '<=': lambda x, y: strcmp(x, y) <= 0, + '>': lambda x, y: strcmp(x, y) > 0, + '>=': lambda x, y: strcmp(x, y) >= 0, + 'in': lambda x, y: re.search(x, y, flags=re.I), + 'inlist': lambda x, y: list(filter(partial(re.search, x, flags=re.I), [v.strip() for v in y.split(',') if v.strip()])) } @@ -1434,12 +1434,12 @@ class _Interpreter: "operator '{0}'").format(prog.operator), prog.line_number) INFIX_NUMERIC_COMPARE_OPS = { - "==#": lambda x, y: x == y, - "!=#": lambda x, y: x != y, - "<#": lambda x, y: x < y, - "<=#": lambda x, y: x <= y, - ">#": lambda x, y: x > y, - ">=#": lambda x, y: x >= y, + '==#': lambda x, y: x == y, + '!=#': lambda x, y: x != y, + '<#': lambda x, y: x < y, + '<=#': lambda x, y: x <= y, + '>#': lambda x, y: x > y, + '>=#': lambda x, y: x >= y, } def float_deal_with_none(self, v): @@ -1804,7 +1804,7 @@ class TemplateFormatter(string.Formatter): raise ValueError( _('Syntax error on line {0} column {1}: text {2}').format(e.lineno, e.offset, e.text)) except KeyError: - raise ValueError(_("The {0} function is not defined in the template").format('evaluate')) + raise ValueError(_('The {0} function is not defined in the template').format('evaluate')) # ################# Override parent classes methods ##################### @@ -1823,13 +1823,13 @@ class TemplateFormatter(string.Formatter): # Handle functions # First see if we have a functional-style expression - if fmt.startswith('\''): + if fmt.startswith("'"): p = 0 else: - p = fmt.find(':\'') + p = fmt.find(":'") if p >= 0: p += 1 - if p >= 0 and fmt[-1] == '\'': + if p >= 0 and fmt[-1] == "'": val = self._eval_program(val, fmt[p+1:-1], None, self.global_vars, None) colon = fmt[0:p].find(':') if colon < 0: diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index 87ec727116..3e9988d989 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -722,7 +722,7 @@ separated by ``separator``. def evaluate(self, formatter, kwargs, mi, locals, name, separator): res = getattr(mi, name, None) if not isinstance(res, list): - return "%s is not a list" % name + return '%s is not a list' % name return separator.join(res) @@ -998,7 +998,7 @@ return ``found_val``, otherwise return ``not_found_val``. If ``found_val`` and fv = args[0] nfv = args[1] else: - raise ValueError(_("{} requires 2 or 4 arguments").format(self.name)) + raise ValueError(_('{} requires 2 or 4 arguments').format(self.name)) l = [v.strip() for v in val.split(',') if v.strip()] (id_, __, regexp) = ident.partition(':') @@ -1952,7 +1952,7 @@ range(1, 5, 2, 1) -> error(limit exceeded) r = range(start_val, stop_val, step_val) if len(r) > limit_val: raise ValueError( - _("{0}: length ({1}) longer than limit ({2})").format( + _('{0}: length ({1}) longer than limit ({2})').format( 'range', len(r), str(limit_val))) return ', '.join([str(v) for v in r]) @@ -2041,8 +2041,8 @@ by ``separator``, as are the items in the returned list. def evaluate(self, formatter, kwargs, mi, locals, value, direction, separator): res = [l.strip() for l in value.split(separator) if l.strip()] if separator == ',': - return ', '.join(sorted(res, key=sort_key, reverse=direction != "0")) - return separator.join(sorted(res, key=sort_key, reverse=direction != "0")) + return ', '.join(sorted(res, key=sort_key, reverse=direction != '0')) + return separator.join(sorted(res, key=sort_key, reverse=direction != '0')) class BuiltinListEquals(BuiltinFormatterFunction): @@ -2227,7 +2227,7 @@ Example: ``'1s3d-1m'`` will add 1 second, add 3 days, and subtract 1 minute from raise e except Exception as e: traceback.print_exc() - raise ValueError(_("{0}: error: {1}").format('date_arithmetic', str(e))) + raise ValueError(_('{0}: error: {1}').format('date_arithmetic', str(e))) class BuiltinLanguageStrings(BuiltinFormatterFunction): @@ -2626,14 +2626,14 @@ This function works only in the GUI and the content server. if res is None: if is_undefined == '1': return 'Yes' - return "" + return '' if not isinstance(res, bool): raise ValueError(_('check_yes_no requires the field be a Yes/No custom column')) if is_false == '1' and not res: return 'Yes' if is_true == '1' and res: return 'Yes' - return "" + return '' class BuiltinRatingToStars(BuiltinFormatterFunction): diff --git a/src/calibre/utils/icu_test.py b/src/calibre/utils/icu_test.py index ac7f519d5f..f3da84f887 100644 --- a/src/calibre/utils/icu_test.py +++ b/src/calibre/utils/icu_test.py @@ -85,7 +85,7 @@ class TestICU(unittest.TestCase): for x in ('', None, False, 1): self.ae(x, icu.capitalize(x)) - for x in ('a', 'Alice\'s code', 'macdonald\'s machIne', '02 the wars'): + for x in ('a', "Alice's code", "macdonald's machIne", '02 the wars'): self.ae(icu.upper(x), x.upper()) self.ae(icu.lower(x), x.lower()) # ICU's title case algorithm is different from ours, when there are @@ -116,7 +116,7 @@ class TestICU(unittest.TestCase): x = icu.primary_collator() self.ae(x.get_attribute(icu._icu.UCOL_STRENGTH), icu._icu.UCOL_PRIMARY), self.ae((0, 4), icu.primary_no_punc_find('pena"', 'peña')) - self.ae((0, 13), icu.primary_no_punc_find("typographers", 'typographer’s')) + self.ae((0, 13), icu.primary_no_punc_find('typographers', 'typographer’s')) self.ae((0, 7), icu.primary_no_punc_find('abcd', 'a\u00adb\u200cc\u200dd')) self.ae((0, 5), icu.primary_no_punc_find('abcd', 'ab cd')) # test find all @@ -206,7 +206,7 @@ class TestICU(unittest.TestCase): from calibre.spell.break_iterator import split_into_words as split for q in ('one two three', ' one two three', 'one\ntwo three ', ): self.ae(split(str(q)), ['one', 'two', 'three'], 'Failed to split: %r' % q) - self.ae(split('I I\'m'), ['I', "I'm"]) + self.ae(split("I I'm"), ['I', "I'm"]) self.ae(split('out-of-the-box'), ['out-of-the-box']) self.ae(split('-one two-'), ['-one', 'two-']) self.ae(split('-one a-b-c-d e'), ['-one', 'a-b-c-d', 'e']) diff --git a/src/calibre/utils/img.py b/src/calibre/utils/img.py index f45c3ad5f3..dc20adebe2 100644 --- a/src/calibre/utils/img.py +++ b/src/calibre/utils/img.py @@ -664,11 +664,11 @@ def encode_webp(file_path, quality=75, m=6, metadata='all'): # PIL images {{{ def align8to32(bytes, width, mode): - """ + ''' converts each scanline of data from 8 bit to 32 bit aligned - """ + ''' - bits_per_pixel = {"1": 1, "L": 8, "P": 8, "I;16": 16}[mode] + bits_per_pixel = {'1': 1, 'L': 8, 'P': 8, 'I;16': 16}[mode] # calculate bytes per line and the extra padding if needed bits_per_line = bits_per_pixel * width @@ -682,34 +682,34 @@ def align8to32(bytes, width, mode): return bytes new_data = [ - bytes[i * bytes_per_line : (i + 1) * bytes_per_line] + b"\x00" * extra_padding + bytes[i * bytes_per_line : (i + 1) * bytes_per_line] + b'\x00' * extra_padding for i in range(len(bytes) // bytes_per_line) ] - return b"".join(new_data) + return b''.join(new_data) def convert_PIL_image_to_pixmap(im, device_pixel_ratio=1.0): data = None colortable = None - if im.mode == "RGBA": + if im.mode == 'RGBA': fmt = QImage.Format.Format_RGBA8888 - data = im.tobytes("raw", "RGBA") - elif im.mode == "1": + data = im.tobytes('raw', 'RGBA') + elif im.mode == '1': fmt = QImage.Format.Format_Mono - elif im.mode == "L": + elif im.mode == 'L': fmt = QImage.Format.Format_Indexed8 colortable = [qRgba(i, i, i, 255) & 0xFFFFFFFF for i in range(256)] - elif im.mode == "P": + elif im.mode == 'P': fmt = QImage.Format.Format_Indexed8 palette = im.getpalette() colortable = [qRgba(*palette[i : i + 3], 255) & 0xFFFFFFFF for i in range(0, len(palette), 3)] - elif im.mode == "I;16": + elif im.mode == 'I;16': im = im.point(lambda i: i * 256) fmt = QImage.Format.Format_Grayscale16 else: fmt = QImage.Format.Format_RGBX8888 - data = im.convert("RGBA").tobytes("raw", "RGBA") + data = im.convert('RGBA').tobytes('raw', 'RGBA') size = im.size data = data or align8to32(im.tobytes(), size[0], im.mode) @@ -726,15 +726,15 @@ def read_xmp_from_pil_image(im) -> str: xml = '' if fmt == 'jpeg': for segment, content in im.applist: - if segment == "APP1": - marker, xmp_tags = content.split(b"\x00")[:2] - if marker == b"http://ns.adobe.com/xap/1.0/": + if segment == 'APP1': + marker, xmp_tags = content.split(b'\x00')[:2] + if marker == b'http://ns.adobe.com/xap/1.0/': xml = xmp_tags break elif fmt == 'png': xml = im.info.get('XML:com.adobe.xmp', '') elif fmt == 'webp': - xml = im.info.get("xmp", '') + xml = im.info.get('xmp', '') elif fmt == 'tiff': xml = im.tag_v2.get(700, '') return xml diff --git a/src/calibre/utils/imghdr.py b/src/calibre/utils/imghdr.py index 186b71c081..9b822b1c04 100644 --- a/src/calibre/utils/imghdr.py +++ b/src/calibre/utils/imghdr.py @@ -8,7 +8,7 @@ from struct import error, unpack from calibre.utils.speedups import ReadOnlyFileBuffer from polyglot.builtins import string_or_bytes -""" Recognize image file formats and sizes based on their first few bytes.""" +''' Recognize image file formats and sizes based on their first few bytes.''' HSIZE = 120 @@ -70,7 +70,7 @@ def _identify(stream): # PNG s = head[16:24] if size >= 24 and head[12:16] == b'IHDR' else head[8:16] try: - width, height = unpack(b">LL", s) + width, height = unpack(b'>LL', s) except error: return fmt, width, height elif fmt == 'jpeg': @@ -85,7 +85,7 @@ def _identify(stream): elif fmt == 'gif': # GIF try: - width, height = unpack(b"<HH", head[6:10]) + width, height = unpack(b'<HH', head[6:10]) except error: return fmt, width, height elif size >= 56 and fmt == 'jpeg2000': @@ -111,9 +111,9 @@ def test(f): @test def jpeg(h): - """JPEG data in JFIF format (Changed by Kovid to mimic the file utility, + '''JPEG data in JFIF format (Changed by Kovid to mimic the file utility, the original code was failing with some jpegs that included ICC_PROFILE - data, for example: http://nationalpostnews.files.wordpress.com/2013/03/budget.jpeg?w=300&h=1571)""" + data, for example: http://nationalpostnews.files.wordpress.com/2013/03/budget.jpeg?w=300&h=1571)''' if h[6:10] in (b'JFIF', b'Exif'): return 'jpeg' if h[:2] == b'\xff\xd8': @@ -169,7 +169,7 @@ def jpeg_dimensions(stream): @test def png(h): - if h[:8] == b"\211PNG\r\n\032\n": + if h[:8] == b'\211PNG\r\n\032\n': return 'png' @@ -182,7 +182,7 @@ def gif(h): @test def tiff(h): - """TIFF (can be in Motorola or Intel byte order)""" + '''TIFF (can be in Motorola or Intel byte order)''' if h[:2] in (b'MM', b'II'): if h[2:4] == b'\xbc\x01': return 'jxr' @@ -197,14 +197,14 @@ def webp(h): @test def rgb(h): - """SGI image library""" + '''SGI image library''' if h[:2] == b'\001\332': return 'rgb' @test def pbm(h): - """PBM (portable bitmap)""" + '''PBM (portable bitmap)''' if len(h) >= 3 and \ h[0] == b'P' and h[1] in b'14' and h[2] in b' \t\n\r': return 'pbm' @@ -212,7 +212,7 @@ def pbm(h): @test def pgm(h): - """PGM (portable graymap)""" + '''PGM (portable graymap)''' if len(h) >= 3 and \ h[0] == b'P' and h[1] in b'25' and h[2] in b' \t\n\r': return 'pgm' @@ -220,7 +220,7 @@ def pgm(h): @test def ppm(h): - """PPM (portable pixmap)""" + '''PPM (portable pixmap)''' if len(h) >= 3 and \ h[0] == b'P' and h[1] in b'36' and h[2] in b' \t\n\r': return 'ppm' @@ -228,14 +228,14 @@ def ppm(h): @test def rast(h): - """Sun raster file""" + '''Sun raster file''' if h[:4] == b'\x59\xA6\x6A\x95': return 'rast' @test def xbm(h): - """X bitmap (X10 or X11)""" + '''X bitmap (X10 or X11)''' s = b'#define ' if h[:len(s)] == s: return 'xbm' diff --git a/src/calibre/utils/inotify.py b/src/calibre/utils/inotify.py index 6941215999..fb2a8b701c 100644 --- a/src/calibre/utils/inotify.py +++ b/src/calibre/utils/inotify.py @@ -47,27 +47,27 @@ def load_inotify(): # {{{ if not hasattr(ctypes, 'c_ssize_t'): raise INotifyError('You need python >= 2.7 to use inotify') libc = ctypes.CDLL(None, use_errno=True) - for function in ("inotify_add_watch", "inotify_init1", "inotify_rm_watch"): + for function in ('inotify_add_watch', 'inotify_init1', 'inotify_rm_watch'): if not hasattr(libc, function): raise INotifyError('libc is too old') # inotify_init1() prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, use_errno=True) - init1 = prototype(('inotify_init1', libc), ((1, "flags", 0),)) + init1 = prototype(('inotify_init1', libc), ((1, 'flags', 0),)) # inotify_add_watch() prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.c_uint32, use_errno=True) add_watch = prototype(('inotify_add_watch', libc), ( - (1, "fd"), (1, "pathname"), (1, "mask")), use_errno=True) + (1, 'fd'), (1, 'pathname'), (1, 'mask')), use_errno=True) # inotify_rm_watch() prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, use_errno=True) rm_watch = prototype(('inotify_rm_watch', libc), ( - (1, "fd"), (1, "wd")), use_errno=True) + (1, 'fd'), (1, 'wd')), use_errno=True) # read() prototype = ctypes.CFUNCTYPE(ctypes.c_ssize_t, ctypes.c_int, ctypes.c_void_p, ctypes.c_size_t, use_errno=True) read = prototype(('read', libc), ( - (1, "fd"), (1, "buf"), (1, "count")), use_errno=True) + (1, 'fd'), (1, 'buf'), (1, 'count')), use_errno=True) _inotify = (init1, add_watch, rm_watch, read) return _inotify # }}} diff --git a/src/calibre/utils/iphlpapi.py b/src/calibre/utils/iphlpapi.py index 7295aca1aa..06d68259b9 100644 --- a/src/calibre/utils/iphlpapi.py +++ b/src/calibre/utils/iphlpapi.py @@ -13,10 +13,10 @@ from ctypes import windll, wintypes class GUID(ctypes.Structure): _fields_ = [ - ("data1", wintypes.DWORD), - ("data2", wintypes.WORD), - ("data3", wintypes.WORD), - ("data4", wintypes.BYTE * 8)] + ('data1', wintypes.DWORD), + ('data2', wintypes.WORD), + ('data3', wintypes.WORD), + ('data4', wintypes.BYTE * 8)] def __init__(self, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8): self.data1 = l diff --git a/src/calibre/utils/ipython.py b/src/calibre/utils/ipython.py index 1efa73232b..d9a6e2a40c 100644 --- a/src/calibre/utils/ipython.py +++ b/src/calibre/utils/ipython.py @@ -126,7 +126,7 @@ history_length(2000) #value of -1 means no limit # if you need to change this uncomment the following line # pyreadline.unicode_helper.pyreadline_codepage="utf8" except ImportError: - print("Module readline not available.") + print('Module readline not available.') else: # import tab completion functionality import rlcompleter @@ -140,7 +140,7 @@ history_length(2000) #value of -1 means no limit readline.set_completer(completer_obj.complete) # activate tab completion - readline.parse_and_bind("tab: complete") + readline.parse_and_bind('tab: complete') readline.read_history_file() atexit.register(readline.write_history_file) del readline, rlcompleter, atexit @@ -159,8 +159,8 @@ class Exit: class Helper: def __repr__(self): - return "Type help() for interactive help, " \ - "or help(object) for help about object." + return 'Type help() for interactive help, ' \ + 'or help(object) for help about object.' def __call__(self, *args, **kwds): import pydoc @@ -173,7 +173,7 @@ def simple_repl(user_ns={}): else: try: import readline, rlcompleter # noqa: I001, E401, F401 - readline.parse_and_bind("tab: complete") + readline.parse_and_bind('tab: complete') except ImportError: pass diff --git a/src/calibre/utils/iso8601.py b/src/calibre/utils/iso8601.py index f4733d80e5..6e40b3a579 100644 --- a/src/calibre/utils/iso8601.py +++ b/src/calibre/utils/iso8601.py @@ -21,7 +21,7 @@ def parse_iso8601(date_string, assume_utc=False, as_utc=True, require_aware=Fals tz = utc_tz else: sign = '-' if tzseconds < 0 else '+' - description = "%s%02d:%02d" % (sign, abs(tzseconds) // 3600, (abs(tzseconds) % 3600) // 60) + description = '%s%02d:%02d' % (sign, abs(tzseconds) // 3600, (abs(tzseconds) % 3600) // 60) tz = timezone(timedelta(seconds=tzseconds), description) elif require_aware: raise ValueError(f'{date_string} does not specify a time zone') diff --git a/src/calibre/utils/linux_trash.py b/src/calibre/utils/linux_trash.py index ebc4eea4bd..7d7e9b8a42 100644 --- a/src/calibre/utils/linux_trash.py +++ b/src/calibre/utils/linux_trash.py @@ -51,7 +51,7 @@ def is_parent(parent, path): def format_date(date): - return date.strftime("%Y-%m-%dT%H:%M:%S") + return date.strftime('%Y-%m-%dT%H:%M:%S') def info_for(src, topdir): @@ -62,9 +62,9 @@ def info_for(src, topdir): else: src = op.relpath(src, topdir) - info = "[Trash Info]\n" - info += "Path=" + uniquote(src) + "\n" - info += "DeletionDate=" + format_date(datetime.now()) + "\n" + info = '[Trash Info]\n' + info += 'Path=' + uniquote(src) + '\n' + info += 'DeletionDate=' + format_date(datetime.now()) + '\n' return info @@ -151,11 +151,11 @@ def get_dev(path): def send2trash(path): if not op.exists(path): - raise OSError("File not found: %s" % path) + raise OSError('File not found: %s' % path) # ...should check whether the user has the necessary permissions to delete # it, before starting the trashing operation itself. [2] if not os.access(path, os.W_OK): - raise OSError("Permission denied: %s" % path) + raise OSError('Permission denied: %s' % path) # if the file to be trashed is on the same device as HOMETRASH we # want to move it there. path_dev = get_dev(path) diff --git a/src/calibre/utils/matcher.py b/src/calibre/utils/matcher.py index c08fee8e58..f9300de916 100644 --- a/src/calibre/utils/matcher.py +++ b/src/calibre/utils/matcher.py @@ -277,7 +277,7 @@ def test(return_tests=False): class Test(unittest.TestCase): - @unittest.skipIf(is_sanitized, 'Sanitizer enabled can\'t check for leaks') + @unittest.skipIf(is_sanitized, "Sanitizer enabled can't check for leaks") def test_mem_leaks(self): import gc diff --git a/src/calibre/utils/mem.py b/src/calibre/utils/mem.py index ce286b04ca..6b862a0d81 100644 --- a/src/calibre/utils/mem.py +++ b/src/calibre/utils/mem.py @@ -32,7 +32,7 @@ def memory(since=0.0): def gc_histogram(): - """Returns per-class counts of existing objects.""" + '''Returns per-class counts of existing objects.''' result = {} for o in gc.get_objects(): t = type(o) @@ -42,10 +42,10 @@ def gc_histogram(): def diff_hists(h1, h2): - """Prints differences between two results of gc_histogram().""" + '''Prints differences between two results of gc_histogram().''' for k in h1: if k not in h2: h2[k] = 0 if h1[k] != h2[k]: - print("%s: %d -> %d (%s%d)" % ( - k, h1[k], h2[k], h2[k] > h1[k] and "+" or "", h2[k] - h1[k])) + print('%s: %d -> %d (%s%d)' % ( + k, h1[k], h2[k], h2[k] > h1[k] and '+' or '', h2[k] - h1[k])) diff --git a/src/calibre/utils/mreplace.py b/src/calibre/utils/mreplace.py index 65b9cdcd65..2c9bbb63b6 100644 --- a/src/calibre/utils/mreplace.py +++ b/src/calibre/utils/mreplace.py @@ -25,9 +25,9 @@ class MReplace(UserDict): if len(self.data) > 0: keys = sorted(self.data, key=len, reverse=True) if isinstance(keys[0], bytes): - tmp = b"(%s)" % b"|".join(map(re.escape, keys)) + tmp = b'(%s)' % b'|'.join(map(re.escape, keys)) else: - tmp = "(%s)" % "|".join(map(re.escape, keys)) + tmp = '(%s)' % '|'.join(map(re.escape, keys)) if self.re != tmp: self.re = tmp if self.case_sensitive: diff --git a/src/calibre/utils/network.py b/src/calibre/utils/network.py index 213625c950..d93e98f736 100644 --- a/src/calibre/utils/network.py +++ b/src/calibre/utils/network.py @@ -30,10 +30,10 @@ class LinuxNetworkStatus: self.xdp_call = lambda : new_method_call(DBusAddress( '/org/freedesktop/portal/desktop', bus_name='org.freedesktop.portal.Desktop', - interface="org.freedesktop.portal.NetworkMonitor"), 'GetConnectivity') + interface='org.freedesktop.portal.NetworkMonitor'), 'GetConnectivity') self.nm_call = lambda : Properties(DBusAddress('/org/freedesktop/NetworkManager', bus_name='org.freedesktop.NetworkManager', - interface="org.freedesktop.NetworkManager")).get('Connectivity') + interface='org.freedesktop.NetworkManager')).get('Connectivity') if self.xdp() is not None: self.get_connectivity = self.xdp diff --git a/src/calibre/utils/opensearch/description.py b/src/calibre/utils/opensearch/description.py index f1bada0926..fa2b1ebd53 100644 --- a/src/calibre/utils/opensearch/description.py +++ b/src/calibre/utils/opensearch/description.py @@ -17,7 +17,7 @@ class Description: A class for representing OpenSearch Description files. ''' - def __init__(self, url=""): + def __init__(self, url=''): ''' The constructor which may pass an optional url to load from. diff --git a/src/calibre/utils/podofo/__init__.py b/src/calibre/utils/podofo/__init__.py index 40e27e9f3a..28c168d57f 100644 --- a/src/calibre/utils/podofo/__init__.py +++ b/src/calibre/utils/podofo/__init__.py @@ -238,7 +238,7 @@ def test_podofo(): fraw = f.read() wraw = p.write() if fraw != wraw: - raise ValueError("write() and save_to_fileobj() resulted in different output") + raise ValueError('write() and save_to_fileobj() resulted in different output') try: p = podofo.PDFDoc() p.open(f.name) diff --git a/src/calibre/utils/rapydscript.py b/src/calibre/utils/rapydscript.py index c46f582410..2d76214f7d 100644 --- a/src/calibre/utils/rapydscript.py +++ b/src/calibre/utils/rapydscript.py @@ -394,7 +394,7 @@ def run_rapydscript_tests(): if fail_code is None: fail_code = QWebEngineUrlRequestJob.Error.UrlNotFound rq.fail(fail_code) - print(f"Blocking FAKE_PROTOCOL request: {rq.requestUrl().toString()}", file=sys.stderr) + print(f'Blocking FAKE_PROTOCOL request: {rq.requestUrl().toString()}', file=sys.stderr) class Tester(QWebEnginePage): diff --git a/src/calibre/utils/search_query_parser.py b/src/calibre/utils/search_query_parser.py index e7b47940aa..08b887a6f8 100644 --- a/src/calibre/utils/search_query_parser.py +++ b/src/calibre/utils/search_query_parser.py @@ -294,7 +294,7 @@ class ParseException(Exception): def msg(self): if len(self.args) > 0: return self.args[0] - return "" + return '' class SearchQueryParser: diff --git a/src/calibre/utils/shm.py b/src/calibre/utils/shm.py index c7a64adb42..c2fafa44dc 100644 --- a/src/calibre/utils/shm.py +++ b/src/calibre/utils/shm.py @@ -19,7 +19,7 @@ else: def make_filename(prefix: str) -> str: - "Create a random filename for the shared memory object." + 'Create a random filename for the shared memory object.' # number of random bytes to use for name. Use a largeish value # to make double unlink safe. if not iswindows and not prefix.startswith('/'): @@ -202,8 +202,8 @@ class SharedMemory: return f'{self.__class__.__name__}({self.name!r}, size={self.size})' def close(self) -> None: - """Closes access to the shared memory from this instance but does - not destroy the shared memory block.""" + '''Closes access to the shared memory from this instance but does + not destroy the shared memory block.''' if self._mmap is not None: self._mmap.close() self._mmap = None @@ -213,11 +213,11 @@ class SharedMemory: self.unlink() def unlink(self) -> None: - """Requests that the underlying shared memory block be destroyed. + '''Requests that the underlying shared memory block be destroyed. In order to ensure proper cleanup of resources, unlink should be called once (and only once) across all processes which have access - to the shared memory block.""" + to the shared memory block.''' if self._name: if not iswindows: try: diff --git a/src/calibre/utils/smartypants.py b/src/calibre/utils/smartypants.py index aa6408f271..b3bcbe8799 100644 --- a/src/calibre/utils/smartypants.py +++ b/src/calibre/utils/smartypants.py @@ -1,10 +1,10 @@ #!/usr/bin/env python -__author__ = "Chad Miller <smartypantspy@chad.org>, Kovid Goyal <kovid at kovidgoyal.net>" -__description__ = "Smart-quotes, smart-ellipses, and smart-dashes for weblog entries in pyblosxom" +__author__ = 'Chad Miller <smartypantspy@chad.org>, Kovid Goyal <kovid at kovidgoyal.net>' +__description__ = 'Smart-quotes, smart-ellipses, and smart-dashes for weblog entries in pyblosxom' -r""" +r''' ============== smartypants.py ============== @@ -374,12 +374,12 @@ smartypants.py license:: .. _SmartyPants: https://daringfireball.net/projects/smartypants/ .. _Movable Type: http://www.movabletype.org/ -""" +''' import re # style added by Kovid -tags_to_skip_regex = re.compile(r"<(/)?(style|pre|code|kbd|script|math)[^>]*>", re.I) +tags_to_skip_regex = re.compile(r'<(/)?(style|pre|code|kbd|script|math)[^>]*>', re.I) self_closing_regex = re.compile(r'/\s*>$') @@ -388,41 +388,41 @@ self_closing_regex = re.compile(r'/\s*>$') def parse_attr(attr): do_dashes = do_backticks = do_quotes = do_ellipses = do_stupefy = 0 - if attr == "1": + if attr == '1': do_quotes = 1 do_backticks = 1 do_dashes = 1 do_ellipses = 1 - elif attr == "2": + elif attr == '2': # Do everything, turn all options on, use old school dash shorthand. do_quotes = 1 do_backticks = 1 do_dashes = 2 do_ellipses = 1 - elif attr == "3": + elif attr == '3': # Do everything, turn all options on, use inverted old school dash shorthand. do_quotes = 1 do_backticks = 1 do_dashes = 3 do_ellipses = 1 - elif attr == "-1": + elif attr == '-1': # Special "stupefy" mode. do_stupefy = 1 else: for c in attr: - if c == "q": + if c == 'q': do_quotes = 1 - elif c == "b": + elif c == 'b': do_backticks = 1 - elif c == "B": + elif c == 'B': do_backticks = 2 - elif c == "d": + elif c == 'd': do_dashes = 1 - elif c == "D": + elif c == 'D': do_dashes = 2 - elif c == "i": + elif c == 'i': do_dashes = 3 - elif c == "e": + elif c == 'e': do_ellipses = 1 else: pass @@ -445,7 +445,7 @@ def smartyPants(text, attr='1'): # i : inverted old school dashes # e : ellipses - if attr == "0": + if attr == '0': # Do nothing. return text @@ -459,7 +459,7 @@ def smartyPants(text, attr='1'): result = [] in_pre = False - prev_token_last_char = "" + prev_token_last_char = '' # This is a cheat, used to get some context # for one-character tokens that consist of # just a quote char. What we do is remember @@ -468,7 +468,7 @@ def smartyPants(text, attr='1'): # character quote tokens correctly. for cur_token in tokens: - if cur_token[0] == "tag": + if cur_token[0] == 'tag': # Don't mess with quotes inside some tags. This does not handle self <closing/> tags! result.append(cur_token[1]) skip_match = tags_to_skip_regex.match(cur_token[1]) @@ -502,16 +502,16 @@ def smartyPants(text, attr='1'): if do_quotes != 0: if t == "'": # Special case: single-character ' token - if re.match(r"\S", prev_token_last_char): - t = "’" + if re.match(r'\S', prev_token_last_char): + t = '’' else: - t = "‘" + t = '‘' elif t == '"': # Special case: single-character " token - if re.match(r"\S", prev_token_last_char): - t = "”" + if re.match(r'\S', prev_token_last_char): + t = '”' else: - t = "“" + t = '“' else: # Normal case: @@ -522,7 +522,7 @@ def smartyPants(text, attr='1'): prev_token_last_char = last_char result.append(t) - return "".join(result) + return ''.join(result) def educateQuotes(text): @@ -535,36 +535,36 @@ def educateQuotes(text): Example output: “Isn’t this fun?” """ - punct_class = r"""[!"#\$\%'()*+,-.\/:;<=>?\@\[\\\]\^_`{|}~]""" + punct_class = r'''[!"#\$\%'()*+,-.\/:;<=>?\@\[\\\]\^_`{|}~]''' # Special case if the very first character is a quote # followed by punctuation at a non-word-break. Close the quotes by brute force: - text = re.sub(fr"""^'(?={punct_class}\\B)""", r"""’""", text) - text = re.sub(fr"""^"(?={punct_class}\\B)""", r"""”""", text) + text = re.sub(fr'''^'(?={punct_class}\\B)''', r'''’''', text) + text = re.sub(fr'''^"(?={punct_class}\\B)''', r'''”''', text) # Special case for double sets of quotes, e.g.: # <p>He said, "'Quoted' words in a larger quote."</p> - text = re.sub(r""""'(?=\w)""", """“‘""", text) - text = re.sub(r"""'"(?=\w)""", """‘“""", text) - text = re.sub(r'''""(?=\w)''', """““""", text) - text = re.sub(r"""''(?=\w)""", """‘‘""", text) - text = re.sub(r'''\"\'''', """”’""", text) - text = re.sub(r'''\'\"''', """’”""", text) - text = re.sub(r'''""''', """””""", text) - text = re.sub(r"""''""", """’’""", text) + text = re.sub(r'''"'(?=\w)''', '''“‘''', text) + text = re.sub(r''''"(?=\w)''', '''‘“''', text) + text = re.sub(r'''""(?=\w)''', '''““''', text) + text = re.sub(r'''''(?=\w)''', '''‘‘''', text) + text = re.sub(r'''\"\'''', '''”’''', text) + text = re.sub(r'''\'\"''', '''’”''', text) + text = re.sub(r'''""''', '''””''', text) + text = re.sub(r"""''""", '''’’''', text) # Special case for decade abbreviations (the '80s --> ’80s): # See http://practicaltypography.com/apostrophes.html - text = re.sub(r"""(\W|^)'(?=\d{2}s)""", r"""\1’""", text) + text = re.sub(r'''(\W|^)'(?=\d{2}s)''', r'''\1’''', text) # Measurements in feet and inches or longitude/latitude: 19' 43.5" --> 19′ 43.5″ text = re.sub(r'''(\W|^)([-0-9.]+\s*)'(\s*[-0-9.]+)"''', r'\1\2′\3″', text) # Special case for Quotes at inside of other entities, e.g.: # <p>A double quote--"within dashes"--would be nice.</p> - text = re.sub(r"""(?<=\W)"(?=\w)""", r"""“""", text) - text = re.sub(r"""(?<=\W)'(?=\w)""", r"""‘""", text) - text = re.sub(r"""(?<=\w)"(?=\W)""", r"""”""", text) - text = re.sub(r"""(?<=\w)'(?=\W)""", r"""’""", text) + text = re.sub(r'''(?<=\W)"(?=\w)''', r'''“''', text) + text = re.sub(r'''(?<=\W)'(?=\w)''', r'''‘''', text) + text = re.sub(r'''(?<=\w)"(?=\W)''', r'''”''', text) + text = re.sub(r'''(?<=\w)'(?=\W)''', r'''’''', text) # The following are commented out as smartypants tokenizes text by # stripping out html tags. Therefore, there is no guarantee that the @@ -579,11 +579,11 @@ def educateQuotes(text): # text = re.sub(r"""^"(?=\s)""", r"""“""", text) # text = re.sub(r"""^'(?=\s)""", r"""‘""", text) - close_class = r"""[^\ \t\r\n\[\{\(\-]""" - dec_dashes = r"""–|—""" + close_class = r'''[^\ \t\r\n\[\{\(\-]''' + dec_dashes = r'''–|—''' # Get most opening single quotes: - opening_single_quotes_regex = re.compile(r""" + opening_single_quotes_regex = re.compile(r''' ( \s | # a whitespace char, or   | # a non-breaking space entity, or @@ -594,28 +594,28 @@ def educateQuotes(text): ) ' # the quote (?=\w) # followed by a word character - """.format(dec_dashes), re.VERBOSE) - text = opening_single_quotes_regex.sub(r"""\1‘""", text) + '''.format(dec_dashes), re.VERBOSE) + text = opening_single_quotes_regex.sub(r'''\1‘''', text) - closing_single_quotes_regex = re.compile(r""" + closing_single_quotes_regex = re.compile(r''' ({}) ' (?!\s | s\b | \d) - """.format(close_class), re.VERBOSE) - text = closing_single_quotes_regex.sub(r"""\1’""", text) + '''.format(close_class), re.VERBOSE) + text = closing_single_quotes_regex.sub(r'''\1’''', text) - closing_single_quotes_regex = re.compile(r""" + closing_single_quotes_regex = re.compile(r''' ({}) ' (\s | s\b) - """.format(close_class), re.VERBOSE) - text = closing_single_quotes_regex.sub(r"""\1’\2""", text) + '''.format(close_class), re.VERBOSE) + text = closing_single_quotes_regex.sub(r'''\1’\2''', text) # Any remaining single quotes should be opening ones: - text = re.sub(r"""'""", r"""‘""", text) + text = re.sub(r"""'""", r'''‘''', text) # Get most opening double quotes: - opening_double_quotes_regex = re.compile(r""" + opening_double_quotes_regex = re.compile(r''' ( \s | # a whitespace char, or   | # a non-breaking space entity, or @@ -626,29 +626,29 @@ def educateQuotes(text): ) " # the quote (?=\w) # followed by a word character - """.format(dec_dashes), re.VERBOSE) - text = opening_double_quotes_regex.sub(r"""\1“""", text) + '''.format(dec_dashes), re.VERBOSE) + text = opening_double_quotes_regex.sub(r'''\1“''', text) # Double closing quotes: - closing_double_quotes_regex = re.compile(r""" + closing_double_quotes_regex = re.compile(r''' #({})? # character that indicates the quote should be closing " (?=\s) - """.format(close_class), re.VERBOSE) - text = closing_double_quotes_regex.sub(r"""”""", text) + '''.format(close_class), re.VERBOSE) + text = closing_double_quotes_regex.sub(r'''”''', text) - closing_double_quotes_regex = re.compile(r""" + closing_double_quotes_regex = re.compile(r''' ({}) # character that indicates the quote should be closing " - """.format(close_class), re.VERBOSE) - text = closing_double_quotes_regex.sub(r"""\1”""", text) + '''.format(close_class), re.VERBOSE) + text = closing_double_quotes_regex.sub(r'''\1”''', text) if text.endswith('-"'): # A string that endswith -" is sometimes used for dialogue text = text[:-1] + '”' # Any remaining quotes should be opening ones. - text = re.sub(r'"', r"""“""", text) + text = re.sub(r'"', r'''“''', text) return text @@ -662,8 +662,8 @@ def educateBackticks(text): Example output: “Isn't this fun?” """ - text = re.sub(r"""``""", r"""“""", text) - text = re.sub(r"""''""", r"""”""", text) + text = re.sub(r'''``''', r'''“''', text) + text = re.sub(r"""''""", r'''”''', text) return text @@ -677,35 +677,35 @@ def educateSingleBackticks(text): Example output: ‘Isn’t this fun?’ """ - text = re.sub(r"""`""", r"""‘""", text) - text = re.sub(r"""'""", r"""’""", text) + text = re.sub(r'''`''', r'''‘''', text) + text = re.sub(r"""'""", r'''’''', text) return text def educateDashes(text): - """ + ''' Parameter: String. Returns: The string, with each instance of "--" translated to an em-dash HTML entity. - """ + ''' - text = re.sub(r"""---""", r"""–""", text) # en (yes, backwards) - text = re.sub(r"""--""", r"""—""", text) # em (yes, backwards) + text = re.sub(r'''---''', r'''–''', text) # en (yes, backwards) + text = re.sub(r'''--''', r'''—''', text) # em (yes, backwards) return text def educateDashesOldSchool(text): - """ + ''' Parameter: String. Returns: The string, with each instance of "--" translated to an en-dash HTML entity, and each "---" translated to an em-dash HTML entity. - """ + ''' - text = re.sub(r"""---""", r"""—""", text) # em (yes, backwards) - text = re.sub(r"""--""", r"""–""", text) # en (yes, backwards) + text = re.sub(r'''---''', r'''—''', text) # em (yes, backwards) + text = re.sub(r'''--''', r'''–''', text) # en (yes, backwards) return text @@ -724,46 +724,46 @@ def educateDashesOldSchoolInverted(text): the shortcut should be shorter to type. (Thanks to Aaron Swartz for the idea.) """ - text = re.sub(r"""---""", r"""–""", text) # em - text = re.sub(r"""--""", r"""—""", text) # en + text = re.sub(r'''---''', r'''–''', text) # em + text = re.sub(r'''--''', r'''—''', text) # en return text def educateEllipses(text): - """ + ''' Parameter: String. Returns: The string, with each instance of "..." translated to an ellipsis HTML entity. Example input: Huh...? Example output: Huh…? - """ + ''' - text = re.sub(r"""\.\.\.""", r"""…""", text) - text = re.sub(r"""\. \. \.""", r"""…""", text) + text = re.sub(r'''\.\.\.''', r'''…''', text) + text = re.sub(r'''\. \. \.''', r'''…''', text) return text def stupefyEntities(text): - """ + ''' Parameter: String. Returns: The string, with each SmartyPants HTML entity translated to its ASCII counterpart. Example input: “Hello — world.” Example output: "Hello -- world." - """ + ''' - text = re.sub(r"""–""", r"""-""", text) # en-dash - text = re.sub(r"""—""", r"""--""", text) # em-dash + text = re.sub(r'''–''', r'''-''', text) # en-dash + text = re.sub(r'''—''', r'''--''', text) # em-dash - text = re.sub(r"""‘""", r"""'""", text) # open single quote - text = re.sub(r"""’""", r"""'""", text) # close single quote + text = re.sub(r'''‘''', r"""'""", text) # open single quote + text = re.sub(r'''’''', r"""'""", text) # close single quote - text = re.sub(r"""“""", r'''"''', text) # open double quote - text = re.sub(r"""”""", r'''"''', text) # close double quote + text = re.sub(r'''“''', r'''"''', text) # open double quote + text = re.sub(r'''”''', r'''"''', text) # close double quote - text = re.sub(r"""…""", r"""...""", text) # ellipsis + text = re.sub(r'''…''', r'''...''', text) # ellipsis return text @@ -784,12 +784,12 @@ def processEscapes(text): \- - \` ` """ - text = re.sub(r"""\\\\""", r"""\""", text) - text = re.sub(r'''\\"''', r""""""", text) - text = re.sub(r"""\\'""", r"""'""", text) - text = re.sub(r"""\\\.""", r""".""", text) - text = re.sub(r"""\\-""", r"""-""", text) - text = re.sub(r"""\\`""", r"""`""", text) + text = re.sub(r'''\\\\''', r'''\''', text) + text = re.sub(r'''\\"''', r'''"''', text) + text = re.sub(r"""\\'""", r''''''', text) + text = re.sub(r'''\\\.''', r'''.''', text) + text = re.sub(r'''\\-''', r'''-''', text) + text = re.sub(r'''\\`''', r'''`''', text) return text @@ -815,7 +815,7 @@ def _tokenize(html): # match = r"""(?: <! ( -- .*? -- \s* )+ > ) | # comments # (?: <\? .*? \?> ) | # directives # %s # nested tags """ % (nested_tags,) - tag_soup = re.compile(r"""([^<]*)(<[^>]*>)""") + tag_soup = re.compile(r'''([^<]*)(<[^>]*>)''') token_match = tag_soup.search(html) @@ -843,35 +843,35 @@ def run_tests(return_tests=False): # the default attribute is "1", which means "all". def test_dates(self): - self.assertEqual(sp("one two '60s"), "one two ’60s") - self.assertEqual(sp("1440-80's"), "1440-80’s") - self.assertEqual(sp("1440-'80s"), "1440-’80s") - self.assertEqual(sp("1440---'80s"), "1440–’80s") - self.assertEqual(sp("1960s"), "1960s") # no effect. - self.assertEqual(sp("1960's"), "1960’s") - self.assertEqual(sp("one two '60s"), "one two ’60s") - self.assertEqual(sp("'60s"), "’60s") + self.assertEqual(sp("one two '60s"), 'one two ’60s') + self.assertEqual(sp("1440-80's"), '1440-80’s') + self.assertEqual(sp("1440-'80s"), '1440-’80s') + self.assertEqual(sp("1440---'80s"), '1440–’80s') + self.assertEqual(sp('1960s'), '1960s') # no effect. + self.assertEqual(sp("1960's"), '1960’s') + self.assertEqual(sp("one two '60s"), 'one two ’60s') + self.assertEqual(sp("'60s"), '’60s') def test_measurements(self): ae = self.assertEqual - ae(sp("one two 1.1'2.2\""), "one two 1.1′2.2″") - ae(sp("1' 2\""), "1′ 2″") + ae(sp("one two 1.1'2.2\""), 'one two 1.1′2.2″') + ae(sp("1' 2\""), '1′ 2″') def test_skip_tags(self): self.assertEqual( - sp("""<script type="text/javascript">\n<!--\nvar href = "http://www.google.com";\nvar linktext = "google";\ndocument.write('<a href="' + href + '">' + linktext + "</a>");\n//-->\n</script>"""), # noqa: E501 - """<script type="text/javascript">\n<!--\nvar href = "http://www.google.com";\nvar linktext = "google";\ndocument.write('<a href="' + href + '">' + linktext + "</a>");\n//-->\n</script>""") # noqa: E501 + sp('''<script type="text/javascript">\n<!--\nvar href = "http://www.google.com";\nvar linktext = "google";\ndocument.write('<a href="' + href + '">' + linktext + "</a>");\n//-->\n</script>'''), # noqa: E501 + '''<script type="text/javascript">\n<!--\nvar href = "http://www.google.com";\nvar linktext = "google";\ndocument.write('<a href="' + href + '">' + linktext + "</a>");\n//-->\n</script>''') # noqa: E501 self.assertEqual( - sp("""<p>He said "Let's write some code." This code here <code>if True:\n\tprint "Okay"</code> is python code.</p>"""), - """<p>He said “Let’s write some code.” This code here <code>if True:\n\tprint "Okay"</code> is python code.</p>""") # noqa: E501 + sp('''<p>He said "Let's write some code." This code here <code>if True:\n\tprint "Okay"</code> is python code.</p>'''), + '''<p>He said “Let’s write some code.” This code here <code>if True:\n\tprint "Okay"</code> is python code.</p>''') # noqa: E501 self.assertEqual( sp('''<script/><p>It's ok</p>'''), '''<script/><p>It’s ok</p>''') def test_ordinal_numbers(self): - self.assertEqual(sp("21st century"), "21st century") # no effect. - self.assertEqual(sp("3rd"), "3rd") # no effect. + self.assertEqual(sp('21st century'), '21st century') # no effect. + self.assertEqual(sp('3rd'), '3rd') # no effect. def test_educated_quotes(self): self.assertEqual(sp('''"Isn't this fun?"'''), '''“Isn’t this fun?”''') @@ -884,5 +884,5 @@ def run_tests(return_tests=False): unittest.TextTestRunner(verbosity=4).run(tests) -if __name__ == "__main__": +if __name__ == '__main__': run_tests() diff --git a/src/calibre/utils/smtp.py b/src/calibre/utils/smtp.py index 01b697dbb1..e8abdaf931 100644 --- a/src/calibre/utils/smtp.py +++ b/src/calibre/utils/smtp.py @@ -89,7 +89,7 @@ def create_mail(from_, to, subject, text=None, attachment_data=None, outer['To'] = to outer['Subject'] = subject outer['Date'] = formatdate(localtime=True) - outer['Message-Id'] = f"<{uuid.uuid4()}@{get_msgid_domain(from_)}>" + outer['Message-Id'] = f'<{uuid.uuid4()}@{get_msgid_domain(from_)}>' outer.preamble = 'You will not see this in a MIME-aware mail reader.\n' if text is not None: diff --git a/src/calibre/utils/smtplib.py b/src/calibre/utils/smtplib.py index d1742b55ee..15f148dcd5 100644 --- a/src/calibre/utils/smtplib.py +++ b/src/calibre/utils/smtplib.py @@ -54,31 +54,31 @@ from sys import stderr from polyglot.builtins import string_or_bytes -__all__ = ["SMTPException", "SMTPServerDisconnected", "SMTPResponseException", - "SMTPSenderRefused", "SMTPRecipientsRefused", "SMTPDataError", - "SMTPConnectError", "SMTPHeloError", "SMTPAuthenticationError", - "quoteaddr", "quotedata", "SMTP"] +__all__ = ['SMTPException', 'SMTPServerDisconnected', 'SMTPResponseException', + 'SMTPSenderRefused', 'SMTPRecipientsRefused', 'SMTPDataError', + 'SMTPConnectError', 'SMTPHeloError', 'SMTPAuthenticationError', + 'quoteaddr', 'quotedata', 'SMTP'] SMTP_PORT = 25 SMTP_SSL_PORT = 465 -CRLF = "\r\n" +CRLF = '\r\n' _MAXLINE = 8192 # more than 8 times larger than RFC 821, 4.5.3 -OLDSTYLE_AUTH = re.compile(r"auth=(.*)", re.I) +OLDSTYLE_AUTH = re.compile(r'auth=(.*)', re.I) # Exception classes used by this module. class SMTPException(Exception): - """Base class for all exceptions raised by this module.""" + '''Base class for all exceptions raised by this module.''' class SMTPServerDisconnected(SMTPException): - """Not connected to any SMTP server. + '''Not connected to any SMTP server. This exception is raised when the server unexpectedly disconnects, or when an attempt is made to use the SMTP instance before connecting it to a server. - """ + ''' class SMTPResponseException(SMTPException): @@ -128,11 +128,11 @@ class SMTPDataError(SMTPResponseException): class SMTPConnectError(SMTPResponseException): - """Error during connection establishment.""" + '''Error during connection establishment.''' class SMTPHeloError(SMTPResponseException): - """The server refused our HELO reply.""" + '''The server refused our HELO reply.''' class SMTPAuthenticationError(SMTPResponseException): @@ -144,10 +144,10 @@ class SMTPAuthenticationError(SMTPResponseException): def quoteaddr(addr): - """Quote a subset of the email addresses defined by RFC 821. + '''Quote a subset of the email addresses defined by RFC 821. Should be able to handle anything rfc822.parseaddr can handle. - """ + ''' m = (None, None) try: m = email.utils.parseaddr(addr)[1] @@ -155,12 +155,12 @@ def quoteaddr(addr): pass if m == (None, None): # Indicates parse failure or AttributeError # something weird here.. punt -ddm - return "<%s>" % addr + return '<%s>' % addr elif m is None: # the sender wants an empty return address - return "<>" + return '<>' else: - return "<%s>" % m + return '<%s>' % m def _addr_only(addrstring): @@ -187,10 +187,10 @@ except ImportError: _have_ssl = False else: class SSLFakeFile: - """A fake file like object that really wraps a SSLObject. + '''A fake file like object that really wraps a SSLObject. It only supports what is needed in smtplib. - """ + ''' def __init__(self, sslobj): self.sslobj = sslobj @@ -198,9 +198,9 @@ else: def readline(self, size=-1): if size < 0: size = None - str = "" + str = '' chr = None - while chr != "\n": + while chr != '\n': if size is not None and len(str) >= size: break chr = self.sslobj.read(1) @@ -247,7 +247,7 @@ class SMTP: debuglevel = 0 file = None helo_resp = None - ehlo_msg = "ehlo" + ehlo_msg = 'ehlo' ehlo_resp = None does_esmtp = 0 default_port = SMTP_PORT @@ -294,14 +294,14 @@ class SMTP: self.local_hostname = '[%s]' % addr def set_debuglevel(self, debuglevel): - """Set the debug output level. + '''Set the debug output level. A value of 0 means no debug logging. A value of 1 means all interaction with the server is logged except that long lines are truncated to 100 characters and AUTH messages are censored. A value of 2 or higher means the complete session is logged. - """ + ''' self.debuglevel = debuglevel def _get_socket(self, host, port, timeout): @@ -329,7 +329,7 @@ class SMTP: try: port = int(port) except ValueError: - raise OSError("nonnumeric port") + raise OSError('nonnumeric port') if not port: port = self.default_port if self.debuglevel > 0: @@ -338,7 +338,7 @@ class SMTP: self.sock = self._get_socket(host, port, self.timeout) (code, msg) = self.getreply() if self.debuglevel > 0: - self.debug("connect:", msg) + self.debug('connect:', msg) return (code, msg) def send(self, str): @@ -355,9 +355,9 @@ class SMTP: else: raise SMTPServerDisconnected('please run connect() first') - def putcmd(self, cmd, args=""): - """Send a command to the server.""" - if args == "": + def putcmd(self, cmd, args=''): + '''Send a command to the server.''' + if args == '': str = f'{cmd}{CRLF}' else: str = f'{cmd} {args}{CRLF}' @@ -384,14 +384,14 @@ class SMTP: line = self.file.readline(_MAXLINE + 1) except OSError as e: self.close() - raise SMTPServerDisconnected("Connection unexpectedly closed: " + str(e)) + raise SMTPServerDisconnected('Connection unexpectedly closed: ' + str(e)) if line == '': self.close() - raise SMTPServerDisconnected("Connection unexpectedly closed") + raise SMTPServerDisconnected('Connection unexpectedly closed') if self.debuglevel > 0: self.debug('reply:', repr(line)) if len(line) > _MAXLINE: - raise SMTPResponseException(500, "Line too long.") + raise SMTPResponseException(500, 'Line too long.') resp.append(line[4:].strip()) code = line[:3] # Check that the error code is syntactically correct. @@ -402,16 +402,16 @@ class SMTP: errcode = -1 break # Check if multiline response. - if line[3:4] != "-": + if line[3:4] != '-': break - errmsg = "\n".join(resp) + errmsg = '\n'.join(resp) if self.debuglevel > 0: self.debug(f'reply: retcode ({errcode}); Msg: {errmsg}') return errcode, errmsg - def docmd(self, cmd, args=""): - """Send a command, and return its response code.""" + def docmd(self, cmd, args=''): + '''Send a command, and return its response code.''' self.putcmd(cmd, args) return self.getreply() @@ -421,7 +421,7 @@ class SMTP: Hostname to send for this command defaults to the FQDN of the local host. """ - self.putcmd("helo", name or self.local_hostname) + self.putcmd('helo', name or self.local_hostname) (code, msg) = self.getreply() self.helo_resp = msg return (code, msg) @@ -439,7 +439,7 @@ class SMTP: # that happens -ddm if code == -1 and len(msg) == 0: self.close() - raise SMTPServerDisconnected("Server not connected") + raise SMTPServerDisconnected('Server not connected') self.ehlo_resp = msg if code != 250: return (code, msg) @@ -457,8 +457,8 @@ class SMTP: auth_match = OLDSTYLE_AUTH.match(each) if auth_match: # This doesn't remove duplicates, but that's no problem - self.esmtp_features["auth"] = self.esmtp_features.get("auth", "") \ - + " " + auth_match.groups(0)[0] + self.esmtp_features['auth'] = self.esmtp_features.get('auth', '') \ + + ' ' + auth_match.groups(0)[0] continue # RFC 1869 requires a space between ehlo keyword and parameters. @@ -467,39 +467,39 @@ class SMTP: # that the space isn't present if there are no parameters. m = re.match(r'(?P<feature>[A-Za-z0-9][A-Za-z0-9\-]*) ?', each) if m: - feature = m.group("feature").lower() - params = m.string[m.end("feature"):].strip() - if feature == "auth": - self.esmtp_features[feature] = self.esmtp_features.get(feature, "") \ - + " " + params + feature = m.group('feature').lower() + params = m.string[m.end('feature'):].strip() + if feature == 'auth': + self.esmtp_features[feature] = self.esmtp_features.get(feature, '') \ + + ' ' + params else: self.esmtp_features[feature] = params return (code, msg) def has_extn(self, opt): - """Does the server support a given SMTP service extension?""" + '''Does the server support a given SMTP service extension?''' return opt.lower() in self.esmtp_features def help(self, args=''): """SMTP 'help' command. Returns help text from server.""" - self.putcmd("help", args) + self.putcmd('help', args) return self.getreply()[1] def rset(self): """SMTP 'rset' command -- resets session.""" - return self.docmd("rset") + return self.docmd('rset') def noop(self): """SMTP 'noop' command -- doesn't do anything :>""" - return self.docmd("noop") + return self.docmd('noop') def mail(self, sender, options=[]): """SMTP 'mail' command -- begins mail xfer session.""" optionlist = '' if options and self.does_esmtp: optionlist = ' ' + ' '.join(options) - self.putcmd("mail", f"FROM:{quoteaddr(sender)}{optionlist}") + self.putcmd('mail', f'FROM:{quoteaddr(sender)}{optionlist}') return self.getreply() def rcpt(self, recip, options=[]): @@ -507,7 +507,7 @@ class SMTP: optionlist = '' if options and self.does_esmtp: optionlist = ' ' + ' '.join(options) - self.putcmd("rcpt", f"TO:{quoteaddr(recip)}{optionlist}") + self.putcmd('rcpt', f'TO:{quoteaddr(recip)}{optionlist}') return self.getreply() def data(self, msg): @@ -518,33 +518,33 @@ class SMTP: DATA command; the return value from this method is the final response code received when the all data is sent. """ - self.putcmd("data") + self.putcmd('data') (code, repl) = self.getreply() if self.debuglevel > 0: - self.debug("data:", (code, repl)) + self.debug('data:', (code, repl)) if code != 354: raise SMTPDataError(code, repl) else: q = quotedata(msg) if q[-2:] != CRLF: q = q + CRLF - q = q + "." + CRLF + q = q + '.' + CRLF self.send(q) (code, msg) = self.getreply() if self.debuglevel > 0 : - self.debug("data:", (code, msg)) + self.debug('data:', (code, msg)) return (code, msg) def verify(self, address): """SMTP 'verify' command -- checks for address validity.""" - self.putcmd("vrfy", _addr_only(address)) + self.putcmd('vrfy', _addr_only(address)) return self.getreply() # a.k.a. vrfy = verify def expn(self, address): """SMTP 'expn' command -- expands a mailing list.""" - self.putcmd("expn", _addr_only(address)) + self.putcmd('expn', _addr_only(address)) return self.getreply() # some useful methods @@ -592,23 +592,23 @@ class SMTP: challenge = base64.decodestring(challenge) if isinstance(password, str): # Added by Kovid, see http://bugs.python.org/issue5285 password = password.encode('utf-8') - response = user + " " + hmac.HMAC(password, challenge).hexdigest() - return encode_base64(response, eol="") + response = user + ' ' + hmac.HMAC(password, challenge).hexdigest() + return encode_base64(response, eol='') def encode_plain(user, password): - return encode_base64(f"\0{user}\0{password}", eol="") + return encode_base64(f'\0{user}\0{password}', eol='') - AUTH_PLAIN = "PLAIN" - AUTH_CRAM_MD5 = "CRAM-MD5" - AUTH_LOGIN = "LOGIN" + AUTH_PLAIN = 'PLAIN' + AUTH_CRAM_MD5 = 'CRAM-MD5' + AUTH_LOGIN = 'LOGIN' self.ehlo_or_helo_if_needed() - if not self.has_extn("auth"): - raise SMTPException("SMTP AUTH extension not supported by server.") + if not self.has_extn('auth'): + raise SMTPException('SMTP AUTH extension not supported by server.') # Authentication methods the server supports: - authlist = self.esmtp_features["auth"].split() + authlist = self.esmtp_features['auth'].split() # List of authentication methods we support: from preferred to # less preferred methods. Except for the purpose of testing the weaker @@ -623,22 +623,22 @@ class SMTP: break if authmethod == AUTH_CRAM_MD5: - (code, resp) = self.docmd("AUTH", AUTH_CRAM_MD5) + (code, resp) = self.docmd('AUTH', AUTH_CRAM_MD5) if code == 503: # 503 == 'Error: already authenticated' return (code, resp) (code, resp) = self.docmd(encode_cram_md5(resp, user, password)) elif authmethod == AUTH_PLAIN: - (code, resp) = self.docmd("AUTH", - AUTH_PLAIN + " " + encode_plain(user, password)) + (code, resp) = self.docmd('AUTH', + AUTH_PLAIN + ' ' + encode_plain(user, password)) elif authmethod == AUTH_LOGIN: - (code, resp) = self.docmd("AUTH", - "{} {}".format(AUTH_LOGIN, encode_base64(user, eol=""))) + (code, resp) = self.docmd('AUTH', + '{} {}'.format(AUTH_LOGIN, encode_base64(user, eol=''))) if code != 334: raise SMTPAuthenticationError(code, resp) - (code, resp) = self.docmd(encode_base64(password, eol="")) + (code, resp) = self.docmd(encode_base64(password, eol='')) elif authmethod is None: - raise SMTPException("No suitable authentication method found.") + raise SMTPException('No suitable authentication method found.') if code not in (235, 503): # 235 == 'Authentication successful' # 503 == 'Error: already authenticated' @@ -663,12 +663,12 @@ class SMTP: the helo greeting. """ self.ehlo_or_helo_if_needed() - if not self.has_extn("starttls"): - raise SMTPException("STARTTLS extension not supported by server.") - (resp, reply) = self.docmd("STARTTLS") + if not self.has_extn('starttls'): + raise SMTPException('STARTTLS extension not supported by server.') + (resp, reply) = self.docmd('STARTTLS') if resp == 220: if not _have_ssl: - raise RuntimeError("No SSL support included in this Python") + raise RuntimeError('No SSL support included in this Python') if context is None: self.sock = ssl.wrap_socket(self.sock) else: @@ -752,7 +752,7 @@ class SMTP: # Hmmm? what's this? -ddm # self.esmtp_features['7bit']="" if self.has_extn('size'): - esmtp_opts.append("size=%d" % len(msg)) + esmtp_opts.append('size=%d' % len(msg)) for option in mail_options: esmtp_opts.append(option) @@ -779,7 +779,7 @@ class SMTP: return senderrs def close(self): - """Close the connection to the SMTP server.""" + '''Close the connection to the SMTP server.''' try: file = self.file self.file = None @@ -792,8 +792,8 @@ class SMTP: sock.close() def quit(self): - """Terminate the SMTP session.""" - res = self.docmd("quit") + '''Terminate the SMTP session.''' + res = self.docmd('quit') # A new EHLO is required after reconnecting with connect() self.ehlo_resp = self.helo_resp = None self.esmtp_features = {} @@ -832,7 +832,7 @@ if _have_ssl: self.file = SSLFakeFile(new_socket) return new_socket - __all__.append("SMTP_SSL") + __all__.append('SMTP_SSL') # # LMTP extension @@ -853,14 +853,14 @@ class LMTP(SMTP): using a Unix socket, LMTP generally don't support or require any authentication, but your mileage might vary.""" - ehlo_msg = "lhlo" + ehlo_msg = 'lhlo' def __init__(self, host='', port=LMTP_PORT, local_hostname=None): - """Initialize a new instance.""" + '''Initialize a new instance.''' SMTP.__init__(self, host, port, local_hostname) def connect(self, host='localhost', port=0): - """Connect to the LMTP daemon, on either a Unix or a TCP socket.""" + '''Connect to the LMTP daemon, on either a Unix or a TCP socket.''' if host[0] != '/': return SMTP.connect(self, host, port) @@ -877,7 +877,7 @@ class LMTP(SMTP): raise (code, msg) = self.getreply() if self.debuglevel > 0: - self.debug("connect:", msg) + self.debug('connect:', msg) return (code, msg) @@ -887,19 +887,19 @@ if __name__ == '__main__': import sys def prompt(prompt): - sys.stdout.write(prompt + ": ") + sys.stdout.write(prompt + ': ') return sys.stdin.readline().strip() - fromaddr = prompt("From") - toaddrs = prompt("To").split(',') - print("Enter message, end with ^D:") + fromaddr = prompt('From') + toaddrs = prompt('To').split(',') + print('Enter message, end with ^D:') msg = '' while 1: line = sys.stdin.readline() if not line: break msg = msg + line - print("Message length is %d" % len(msg)) + print('Message length is %d' % len(msg)) server = SMTP('localhost') server.set_debuglevel(1) diff --git a/src/calibre/utils/terminal.py b/src/calibre/utils/terminal.py index 6aee59ef56..e348211b73 100644 --- a/src/calibre/utils/terminal.py +++ b/src/calibre/utils/terminal.py @@ -197,7 +197,7 @@ def windows_terminfo(): class COORD(Structure): - """struct in wincon.h""" + '''struct in wincon.h''' _fields_ = [ ('X', SHORT), ('Y', SHORT), @@ -205,23 +205,23 @@ def windows_terminfo(): class SMALL_RECT(Structure): - """struct in wincon.h.""" + '''struct in wincon.h.''' _fields_ = [ - ("Left", SHORT), - ("Top", SHORT), - ("Right", SHORT), - ("Bottom", SHORT), + ('Left', SHORT), + ('Top', SHORT), + ('Right', SHORT), + ('Bottom', SHORT), ] class CONSOLE_SCREEN_BUFFER_INFO(Structure): - """struct in wincon.h.""" + '''struct in wincon.h.''' _fields_ = [ - ("dwSize", COORD), - ("dwCursorPosition", COORD), - ("wAttributes", WORD), - ("srWindow", SMALL_RECT), - ("dwMaximumWindowSize", COORD), + ('dwSize', COORD), + ('dwCursorPosition', COORD), + ('wAttributes', WORD), + ('srWindow', SMALL_RECT), + ('dwMaximumWindowSize', COORD), ] csbi = CONSOLE_SCREEN_BUFFER_INFO() import msvcrt diff --git a/src/calibre/utils/text2int.py b/src/calibre/utils/text2int.py index ff7722a1f1..bc25abd737 100644 --- a/src/calibre/utils/text2int.py +++ b/src/calibre/utils/text2int.py @@ -1,15 +1,15 @@ #!/usr/bin/env python -__author__ = "stackoverflow community" +__author__ = 'stackoverflow community' __docformat__ = 'restructuredtext en' -""" +''' Takes english numeric words and converts them to integers. Returns False if the word isn't a number. implementation courtesy of the stackoverflow community: http://stackoverflow.com/questions/493174/is-there-a-way-to-convert-number-words-to-integers-python -""" +''' import re @@ -19,19 +19,19 @@ numwords = {} def text2int(textnum): if not numwords: - units = ["zero", "one", "two", "three", "four", "five", "six", - "seven", "eight", "nine", "ten", "eleven", "twelve", - "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", - "eighteen", "nineteen"] + units = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', + 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve', + 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', + 'eighteen', 'nineteen'] - tens = ["", "", "twenty", "thirty", "forty", "fifty", "sixty", - "seventy", "eighty", "ninety"] + tens = ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', + 'seventy', 'eighty', 'ninety'] - scales = ["hundred", "thousand", "million", "billion", "trillion", + scales = ['hundred', 'thousand', 'million', 'billion', 'trillion', 'quadrillion', 'quintillion', 'sexillion', 'septillion', 'octillion', 'nonillion', 'decillion'] - numwords["and"] = (1, 0) + numwords['and'] = (1, 0) for idx, word in enumerate(units): numwords[word] = (1, idx) for idx, word in enumerate(tens): @@ -43,14 +43,14 @@ def text2int(textnum): 'eighth':8, 'ninth':9, 'twelfth':12} ordinal_endings = [('ieth', 'y'), ('th', '')] current = result = 0 - tokens = re.split(r"[\s-]+", textnum) + tokens = re.split(r'[\s-]+', textnum) for word in tokens: if word in ordinal_words: scale, increment = (1, ordinal_words[word]) else: for ending, replacement in ordinal_endings: if word.endswith(ending): - word = f"{word[:-len(ending)]}{replacement}" + word = f'{word[:-len(ending)]}{replacement}' if word not in numwords: # raise Exception("Illegal word: " + word) diff --git a/src/calibre/utils/threadpool.py b/src/calibre/utils/threadpool.py index 1bc1db5044..06ad3495c0 100644 --- a/src/calibre/utils/threadpool.py +++ b/src/calibre/utils/threadpool.py @@ -39,10 +39,10 @@ __all__ = [ 'WorkerThread' ] -__author__ = "Christopher Arndt" -__version__ = "1.2.3" -__revision__ = "$Revision: 1.5 $" -__date__ = "$Date: 2006/06/23 12:32:25 $" +__author__ = 'Christopher Arndt' +__version__ = '1.2.3' +__revision__ = '$Revision: 1.5 $' +__date__ = '$Date: 2006/06/23 12:32:25 $' __license__ = 'Python license' # standard library modules @@ -54,30 +54,30 @@ from polyglot import queue class NoResultsPending(Exception): - """All work requests have been processed.""" + '''All work requests have been processed.''' pass class NoWorkersAvailable(Exception): - """No worker threads available to process remaining requests.""" + '''No worker threads available to process remaining requests.''' pass # classes class WorkerThread(threading.Thread): - """Background thread connected to the requests/results queues. + '''Background thread connected to the requests/results queues. A worker thread sits in the background and picks up work requests from one queue and puts the results in another until it is dismissed. - """ + ''' def __init__(self, requestsQueue, resultsQueue, **kwds): - """Set up thread in daemonic mode and start it immediately. + '''Set up thread in daemonic mode and start it immediately. requestsQueue and resultQueue are instances of queue.Queue passed by the ThreadPool class when it creates a new worker thread. - """ + ''' kwds['daemon'] = True threading.Thread.__init__(self, **kwds) self.workRequestQueue = requestsQueue @@ -86,7 +86,7 @@ class WorkerThread(threading.Thread): self.start() def run(self): - """Repeatedly process the job queue until told to exit.""" + '''Repeatedly process the job queue until told to exit.''' while not self._dismissed.isSet(): # thread blocks here, if queue empty @@ -105,23 +105,23 @@ class WorkerThread(threading.Thread): self.resultQueue.put((request, traceback.format_exc())) def dismiss(self): - """Sets a flag to tell the thread to exit when done with current job. - """ + '''Sets a flag to tell the thread to exit when done with current job. + ''' self._dismissed.set() class WorkRequest: - """A request to execute a callable for putting in the request queue later. + '''A request to execute a callable for putting in the request queue later. See the module function makeRequests() for the common case where you want to build several WorkRequests for the same callable but with different arguments for each call. - """ + ''' def __init__(self, callable, args=None, kwds=None, requestID=None, callback=None, exc_callback=None): - """Create a work request for a callable and attach callbacks. + '''Create a work request for a callable and attach callbacks. A work request consists of the callable to be executed by a worker thread, a list of positional arguments, a dictionary @@ -140,7 +140,7 @@ class WorkRequest: requestID, if given, must be hashable since it is used by the ThreadPool object to store the results of that work request in a dictionary. It defaults to the return value of id(self). - """ + ''' if requestID is None: self.requestID = id(self) @@ -148,7 +148,7 @@ class WorkRequest: try: hash(requestID) except TypeError: - raise TypeError("requestID must be hashable.") + raise TypeError('requestID must be hashable.') self.requestID = requestID self.exception = False self.callback = callback @@ -159,19 +159,19 @@ class WorkRequest: class ThreadPool: - """A thread pool, distributing work requests and collecting results. + '''A thread pool, distributing work requests and collecting results. See the module doctring for more information. - """ + ''' def __init__(self, num_workers, q_size=0): - """Set up the thread pool and start num_workers worker threads. + '''Set up the thread pool and start num_workers worker threads. num_workers is the number of worker threads to start initially. If q_size > 0 the size of the work request queue is limited and the thread pool blocks when the queue is full and it tries to put more work requests in it (see putRequest method). - """ + ''' self.requestsQueue = queue.Queue(q_size) self.resultsQueue = queue.Queue() @@ -180,29 +180,29 @@ class ThreadPool: self.createWorkers(num_workers) def createWorkers(self, num_workers): - """Add num_workers worker threads to the pool.""" + '''Add num_workers worker threads to the pool.''' for i in range(num_workers): self.workers.append(WorkerThread(self.requestsQueue, self.resultsQueue)) def dismissWorkers(self, num_workers): - """Tell num_workers worker threads to quit after their current task. - """ + '''Tell num_workers worker threads to quit after their current task. + ''' for i in range(min(num_workers, len(self.workers))): worker = self.workers.pop() worker.dismiss() def putRequest(self, request, block=True, timeout=0): - """Put work request into work queue and save its id for later.""" + '''Put work request into work queue and save its id for later.''' assert isinstance(request, WorkRequest) self.requestsQueue.put(request, block, timeout) self.workRequests[request.requestID] = request def poll(self, block=False): - """Process any new results in the queue.""" + '''Process any new results in the queue.''' while True: # still results pending? @@ -226,7 +226,7 @@ class ThreadPool: break def wait(self, sleep=0): - """Wait for results, blocking until all have arrived.""" + '''Wait for results, blocking until all have arrived.''' while 1: try: @@ -282,16 +282,16 @@ if __name__ == '__main__': result = round(random.random() * data, 5) # just to show off, we throw an exception once in a while if result > 3: - raise RuntimeError("Something extraordinary happened!") + raise RuntimeError('Something extraordinary happened!') return result # this will be called each time a result is available def print_result(request, result): - print(f"**Result: {result} from request #{request.requestID}") + print(f'**Result: {result} from request #{request.requestID}') # this will be called when an exception occurs within a thread def handle_exception(request, exc_info): - print("Exception occurred in request #%s: %s" % + print('Exception occurred in request #%s: %s' % (request.requestID, exc_info[1])) # assemble the arguments for each job to a list... @@ -311,7 +311,7 @@ if __name__ == '__main__': # then we put the work requests in the queue... for req in requests: main.putRequest(req) - print("Work request #%s added." % req.requestID) + print('Work request #%s added.' % req.requestID) # or shorter: # [main.putRequest(req) for req in requests] @@ -325,15 +325,15 @@ if __name__ == '__main__': while 1: try: main.poll() - print("Main thread working...") + print('Main thread working...') time.sleep(0.5) if i == 10: - print("Adding 3 more worker threads...") + print('Adding 3 more worker threads...') main.createWorkers(3) i += 1 except KeyboardInterrupt: - print("Interrupted!") + print('Interrupted!') break except NoResultsPending: - print("All results collected.") + print('All results collected.') break diff --git a/src/calibre/utils/titlecase.py b/src/calibre/utils/titlecase.py index 6002fd12f5..fa5bf665a7 100644 --- a/src/calibre/utils/titlecase.py +++ b/src/calibre/utils/titlecase.py @@ -1,12 +1,12 @@ #!/usr/bin/env python -""" +''' Original Perl version by: John Gruber https://daringfireball.net/ 10 May 2008 Python version by Stuart Colville http://muffinresearch.co.uk Modifications to make it work with non-ascii chars by Kovid Goyal License: http://www.opensource.org/licenses/mit-license.php -""" +''' import re @@ -18,18 +18,18 @@ __all__ = ['titlecase'] __version__ = '0.5' SMALL = 'a|an|and|as|at|but|by|en|for|if|in|of|on|or|the|to|v\\.?|via|vs\\.?' -PUNCT = r"""!"#$%&'‘’()*+,\-‒–—―./:;?@[\\\]_`{|}~""" +PUNCT = r'''!"#$%&'‘’()*+,\-‒–—―./:;?@[\\\]_`{|}~''' SMALL_WORDS = re.compile(r'^(%s)$' % SMALL, re.I) INLINE_PERIOD = re.compile(r'[a-z][.][a-z]', re.I) UC_ELSEWHERE = re.compile(r'[%s]*?[a-zA-Z]+[A-Z]+?' % PUNCT) -CAPFIRST = re.compile(str(r"^[%s]*?(\w)" % PUNCT), flags=re.UNICODE) +CAPFIRST = re.compile(str(r'^[%s]*?(\w)' % PUNCT), flags=re.UNICODE) SMALL_FIRST = re.compile(fr'^([{PUNCT}]*)({SMALL})\b', re.I|re.U) SMALL_LAST = re.compile(fr'\b({SMALL})[{PUNCT}]?$', re.I|re.U) SMALL_AFTER_NUM = re.compile(r'(\d+\s+)(a|an|the)\b', re.I|re.U) SUBPHRASE = re.compile(r'([:.;?!][ ])(%s)' % SMALL) APOS_SECOND = re.compile(r"^[dol]{1}['‘]{1}[a-z]+$", re.I) -UC_INITIALS = re.compile(r"^(?:[A-Z]{1}\.{1}|[A-Z]{1}\.{1}[A-Z]{1})+$") +UC_INITIALS = re.compile(r'^(?:[A-Z]{1}\.{1}|[A-Z]{1}\.{1}[A-Z]{1})+$') _lang = None @@ -86,9 +86,9 @@ def titlecase(text): hyphenated = [] for item in word.split('-'): hyphenated.append(CAPFIRST.sub(lambda m: icu_upper(m.group(0)), item)) - line.append("-".join(hyphenated)) + line.append('-'.join(hyphenated)) - result = "".join(line) + result = ''.join(line) result = SMALL_FIRST.sub(lambda m: '{}{}'.format( m.group(1), diff --git a/src/calibre/utils/unicode_names.py b/src/calibre/utils/unicode_names.py index 6b1f369e06..82421e182a 100644 --- a/src/calibre/utils/unicode_names.py +++ b/src/calibre/utils/unicode_names.py @@ -30,7 +30,7 @@ def html_entities(): def points_for_word(w): - """Returns the set of all codepoints that contain ``word`` in their names""" + '''Returns the set of all codepoints that contain ``word`` in their names''' w = w.lower() ans = points_for_word.cache.get(w) if ans is None: diff --git a/src/calibre/utils/winreg/dde.py b/src/calibre/utils/winreg/dde.py index 058bea3c6d..70c44b83be 100644 --- a/src/calibre/utils/winreg/dde.py +++ b/src/calibre/utils/winreg/dde.py @@ -37,7 +37,7 @@ DML_ERRORS = { 'MEMORY_ERROR': (0x4008, 'A memory allocation has failed.'), - 'NO_CONV_ESTABLISHED': (0x400a, 'A client\'s attempt to establish a conversation has failed.'), + 'NO_CONV_ESTABLISHED': (0x400a, "A client's attempt to establish a conversation has failed."), 'NOTPROCESSED': (0x4009, 'A transaction has failed.'), diff --git a/src/calibre/utils/winreg/lib.py b/src/calibre/utils/winreg/lib.py index c0cc018d29..ef0563f361 100644 --- a/src/calibre/utils/winreg/lib.py +++ b/src/calibre/utils/winreg/lib.py @@ -43,7 +43,7 @@ RRF_ZEROONFAILURE = 0x20000000 class FILETIME(ctypes.Structure): - _fields_ = [("dwLowDateTime", DWORD), ("dwHighDateTime", DWORD)] + _fields_ = [('dwLowDateTime', DWORD), ('dwHighDateTime', DWORD)] def default_errcheck(result, func, args): diff --git a/src/calibre/utils/wordcount.py b/src/calibre/utils/wordcount.py index 61b7440208..66464664d6 100644 --- a/src/calibre/utils/wordcount.py +++ b/src/calibre/utils/wordcount.py @@ -24,13 +24,13 @@ http://ginstrom.com/scribbles/2008/05/17/counting-words-etc-in-an-html-file-with http://ginstrom.com/scribbles/2007/10/06/counting-words-characters-and-asian-characters-with-python/ """ __version__ = 0.1 -__author__ = "Ryan Ginstrom" +__author__ = 'Ryan Ginstrom' IDEOGRAPHIC_SPACE = 0x3000 def is_asian(char): - """Is the character Asian?""" + '''Is the character Asian?''' # 0x3000 is ideographic space (i.e. double-byte space) # Anything over is an Asian character @@ -38,18 +38,18 @@ def is_asian(char): def filter_jchars(c): - """Filters Asian characters to spaces""" + '''Filters Asian characters to spaces''' if is_asian(c): return ' ' return c def nonj_len(word): - """Returns number of non-Asian words in {word} + '''Returns number of non-Asian words in {word} - 日本語AアジアンB -> 2 - hello -> 1 @param word: A word, possibly containing Asian characters - """ + ''' # Here are the steps: # 本spam日eggs # -> [' ', 's', 'p', 'a', 'm', ' ', 'e', 'g', 'g', 's'] @@ -61,10 +61,10 @@ def nonj_len(word): def get_wordcount(text): - """Get the word/character count for text + '''Get the word/character count for text @param text: The text of the segment - """ + ''' characters = len(text) chars_no_spaces = sum(not x.isspace() for x in text) @@ -80,7 +80,7 @@ def get_wordcount(text): def dict2obj(dictionary): - """Transform a dictionary into an object""" + '''Transform a dictionary into an object''' class Obj: def __init__(self, dictionary): @@ -89,5 +89,5 @@ def dict2obj(dictionary): def get_wordcount_obj(text): - """Get the wordcount as an object rather than a dictionary""" + '''Get the wordcount as an object rather than a dictionary''' return dict2obj(get_wordcount(text)) diff --git a/src/calibre/utils/zipfile.py b/src/calibre/utils/zipfile.py index 03c57bc7c6..1e988efdd7 100644 --- a/src/calibre/utils/zipfile.py +++ b/src/calibre/utils/zipfile.py @@ -1,7 +1,7 @@ -""" +''' Read and write ZIP files. Modified by Kovid Goyal to support replacing files in a zip archive, detecting filename encoding, updating zip files, etc. -""" +''' import binascii import io import os @@ -26,8 +26,8 @@ except ImportError: zlib = None crc32 = binascii.crc32 -__all__ = ["BadZipfile", "error", "ZIP_STORED", "ZIP_DEFLATED", "is_zipfile", - "ZipInfo", "ZipFile", "PyZipFile", "LargeZipFile"] +__all__ = ['BadZipfile', 'error', 'ZIP_STORED', 'ZIP_DEFLATED', 'is_zipfile', + 'ZipInfo', 'ZipFile', 'PyZipFile', 'LargeZipFile'] def decode_zip_internal_file_name(fname, flags): @@ -41,10 +41,10 @@ class BadZipfile(Exception): class LargeZipFile(Exception): - """ + ''' Raised when writing a zipfile, the zipfile requires ZIP64 extensions and those extensions are disabled. - """ + ''' error = BadZipfile # The exception raised by this module @@ -67,8 +67,8 @@ ZIP_DEFLATED = 8 # The "end of central directory" structure, magic number, size, and indices # (section V.I in the format document) -structEndArchive = "<4s4H2LH" -stringEndArchive = b"PK\005\006" +structEndArchive = '<4s4H2LH' +stringEndArchive = b'PK\005\006' sizeEndCentDir = struct.calcsize(structEndArchive) _ECD_SIGNATURE = 0 @@ -86,8 +86,8 @@ _ECD_LOCATION = 9 # The "central directory" structure, magic number, size, and indices # of entries in the structure (section V.F in the format document) -structCentralDir = "<4s4B4HL2L5H2L" -stringCentralDir = b"PK\001\002" +structCentralDir = '<4s4B4HL2L5H2L' +stringCentralDir = b'PK\001\002' sizeCentralDir = struct.calcsize(structCentralDir) # indexes of entries in the central directory structure @@ -113,8 +113,8 @@ _CD_LOCAL_HEADER_OFFSET = 18 # The "local file header" structure, magic number, size, and indices # (section V.A in the format document) -structFileHeader = "<4s2B4HL2L2H" -stringFileHeader = b"PK\003\004" +structFileHeader = '<4s2B4HL2L2H' +stringFileHeader = b'PK\003\004' sizeFileHeader = struct.calcsize(structFileHeader) _FH_SIGNATURE = 0 @@ -131,14 +131,14 @@ _FH_FILENAME_LENGTH = 10 _FH_EXTRA_FIELD_LENGTH = 11 # The "Zip64 end of central directory locator" structure, magic number, and size -structEndArchive64Locator = "<4sLQL" -stringEndArchive64Locator = b"PK\x06\x07" +structEndArchive64Locator = '<4sLQL' +stringEndArchive64Locator = b'PK\x06\x07' sizeEndCentDir64Locator = struct.calcsize(structEndArchive64Locator) # The "Zip64 end of central directory" record, magic number, size, and indices # (section V.G in the format document) -structEndArchive64 = "<4sQ2H2L4Q" -stringEndArchive64 = b"PK\x06\x06" +structEndArchive64 = '<4sQ2H2L4Q' +stringEndArchive64 = b'PK\x06\x06' sizeEndCentDir64 = struct.calcsize(structEndArchive64) _CD64_SIGNATURE = 0 @@ -186,16 +186,16 @@ def _check_zipfile(fp): def is_zipfile(filename): - """Quickly see if a file is a ZIP file by checking the magic number. + '''Quickly see if a file is a ZIP file by checking the magic number. The filename argument may be a file or file-like object too. - """ + ''' result = False try: - if hasattr(filename, "read"): + if hasattr(filename, 'read'): result = _check_zipfile(filename) else: - with open(filename, "rb") as fp: + with open(filename, 'rb') as fp: result = _check_zipfile(fp) except OSError: pass @@ -203,9 +203,9 @@ def is_zipfile(filename): def _EndRecData64(fpin, offset, endrec): - """ + ''' Read the ZIP64 end-of-archive records and use that to update endrec - """ + ''' try: fpin.seek(offset - sizeEndCentDir64Locator, 2) except OSError: @@ -219,7 +219,7 @@ def _EndRecData64(fpin, offset, endrec): return endrec if diskno != 0 or disks != 1: - raise BadZipfile("zipfiles that span multiple disks are not supported") + raise BadZipfile('zipfiles that span multiple disks are not supported') # Assume no 'zip64 extensible data' fpin.seek(offset - sizeEndCentDir64Locator - sizeEndCentDir64, 2) @@ -242,10 +242,10 @@ def _EndRecData64(fpin, offset, endrec): def _EndRecData(fpin): - """Return data from the "End of Central Directory" record, or None. + '''Return data from the "End of Central Directory" record, or None. The data is a list of the nine items in the ZIP "End of central dir" - record followed by a tenth item, the file seek offset of this record.""" + record followed by a tenth item, the file seek offset of this record.''' # Determine file size fpin.seek(0, 2) @@ -259,13 +259,13 @@ def _EndRecData(fpin): except OSError: return None data = fpin.read() - if data[0:4] == stringEndArchive and data[-2:] == b"\000\000": + if data[0:4] == stringEndArchive and data[-2:] == b'\000\000': # the signature is correct and there's no comment, unpack structure endrec = struct.unpack(structEndArchive, data) endrec=list(endrec) # Append a blank comment and record start offset - endrec.append(b"") + endrec.append(b'') endrec.append(filesize - sizeEndCentDir) # Try to read the "Zip64 end of central directory" structure @@ -302,7 +302,7 @@ def _EndRecData(fpin): class ZipInfo : - """Class with attributes describing each file in the ZIP archive.""" + '''Class with attributes describing each file in the ZIP archive.''' __slots__ = ( 'orig_filename', @@ -327,7 +327,7 @@ class ZipInfo : 'file_offset', ) - def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)): + def __init__(self, filename='NoName', date_time=(1980,1,1,0,0,0)): self.orig_filename = filename # Original file name in archive # Terminate the file name at the first null byte. Null bytes in file @@ -349,8 +349,8 @@ class ZipInfo : self.date_time = date_time # year, month, day, hour, min, sec # Standard values: self.compress_type = ZIP_STORED # Type of compression for the file - self.comment = b"" # Comment for each file - self.extra = b"" # ZIP extra data + self.comment = b'' # Comment for each file + self.extra = b'' # ZIP extra data if sys.platform == 'win32': self.create_system = 0 # System which created ZIP archive else: @@ -371,7 +371,7 @@ class ZipInfo : # file_size Size of the uncompressed file def FileHeader(self): - """Return the per-file header as a string.""" + '''Return the per-file header as a string.''' dt = self.date_time dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2] dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2) @@ -427,7 +427,7 @@ class ZipInfo : elif ln == 0: counts = () else: - raise RuntimeError("Corrupt extra field %s"%(ln,)) + raise RuntimeError('Corrupt extra field %s'%(ln,)) idx = 0 @@ -449,7 +449,7 @@ class ZipInfo : class _ZipDecrypter: - """Class to handle decryption of files stored within a ZIP archive. + '''Class to handle decryption of files stored within a ZIP archive. ZIP supports a password-based form of encryption. Even though known plaintext attacks have been found against it, it is still useful @@ -459,15 +459,15 @@ class _ZipDecrypter: zd = _ZipDecrypter(mypwd) plain_char = zd(cypher_char) plain_text = map(zd, cypher_text) - """ + ''' def _GenerateCRCTable(): - """Generate a CRC-32 table. + '''Generate a CRC-32 table. ZIP encryption uses the CRC32 one-byte primitive for scrambling some internal keys. We noticed that a direct implementation is faster than relying on binascii.crc32(). - """ + ''' poly = 0xedb88320 table = [0] * 256 for i in range(256): @@ -482,7 +482,7 @@ class _ZipDecrypter: crctable = _GenerateCRCTable() def _crc32(self, ch, crc): - """Compute the CRC32 primitive on one byte.""" + '''Compute the CRC32 primitive on one byte.''' return ((crc >> 8) & 0xffffff) ^ self.crctable[(crc ^ ch) & 0xff] def __init__(self, pwd): @@ -499,7 +499,7 @@ class _ZipDecrypter: self.key2 = self._crc32(((self.key1 >> 24) & 255), self.key2) def __call__(self, c): - """Decrypt a single byte.""" + '''Decrypt a single byte.''' k = self.key2 | 2 c = c ^ (((k * (k^1)) >> 8) & 255) self._UpdateKeys(c) @@ -508,9 +508,9 @@ class _ZipDecrypter: class ZipExtFile(io.BufferedIOBase): - """File-like object for reading an archive member. + '''File-like object for reading an archive member. Is returned by ZipFile.open(). - """ + ''' # Max size supported by decompressor. MAX_N = 1 << 31 - 1 @@ -555,10 +555,10 @@ class ZipExtFile(io.BufferedIOBase): self._expected_crc = None def readline(self, limit=-1): - """Read and return a line from the stream. + '''Read and return a line from the stream. If limit is specified, at most limit bytes will be read. - """ + ''' if not self._universal and limit < 0: # Shortcut common case - newline found in buffer. @@ -605,7 +605,7 @@ class ZipExtFile(io.BufferedIOBase): return line def peek(self, n=1): - """Returns buffered bytes without advancing the position.""" + '''Returns buffered bytes without advancing the position.''' if n > len(self._readbuffer) - self._offset: chunk = self.read(n) self._offset -= len(chunk) @@ -617,9 +617,9 @@ class ZipExtFile(io.BufferedIOBase): return True def read(self, n=-1): - """Read and return up to n bytes. + '''Read and return up to n bytes. If the argument is omitted, None, or negative, data is read and returned until EOF is reached.. - """ + ''' buf = b'' if n is None: n = -1 @@ -642,10 +642,10 @@ class ZipExtFile(io.BufferedIOBase): self._running_crc = crc32(newdata, self._running_crc) & 0xffffffff # Check the CRC if we're at the end of the file if eof and self._running_crc != self._expected_crc: - raise BadZipfile("Bad CRC-32 for file %r" % self.name) + raise BadZipfile('Bad CRC-32 for file %r' % self.name) def read1(self, n): - """Read up to n bytes with at most one read() system call.""" + '''Read up to n bytes with at most one read() system call.''' # Simplify algorithm (branching) by transforming negative n to large n. if n < 0 or n is None: @@ -712,7 +712,7 @@ class ZipExtFile(io.BufferedIOBase): class ZipFile: - """ Class with methods to open, read, write, close, list and update zip files. + ''' Class with methods to open, read, write, close, list and update zip files. z = ZipFile(file, mode="r", compression=ZIP_STORED, allowZip64=False) @@ -724,13 +724,13 @@ class ZipFile: needed, otherwise it will raise an exception when this would be necessary. - """ + ''' fp = None # Set here since __del__ checks it - def __init__(self, file, mode="r", compression=ZIP_DEFLATED, allowZip64=True): - """Open the ZIP file with mode read "r", write "w" or append "a".""" - if mode not in ("r", "w", "a"): + def __init__(self, file, mode='r', compression=ZIP_DEFLATED, allowZip64=True): + '''Open the ZIP file with mode read "r", write "w" or append "a".''' + if mode not in ('r', 'w', 'a'): raise RuntimeError('ZipFile() requires mode "r", "w", or "a" not %s'%mode) if compression == ZIP_STORED: @@ -738,9 +738,9 @@ class ZipFile: elif compression == ZIP_DEFLATED: if not zlib: raise RuntimeError( - "Compression requires the (missing) zlib module") + 'Compression requires the (missing) zlib module') else: - raise RuntimeError("The compression method %s is not supported" % compression) + raise RuntimeError('The compression method %s is not supported' % compression) self._allowZip64 = allowZip64 self._didModify = False @@ -803,8 +803,8 @@ class ZipFile: self.close() def _GetContents(self): - """Read the directory, making sure we close the file if the format - is bad.""" + '''Read the directory, making sure we close the file if the format + is bad.''' try: self._RealGetContents() except BadZipfile: @@ -814,14 +814,14 @@ class ZipFile: raise def _RealGetContents(self): - """Read in the table of contents for the ZIP file.""" + '''Read in the table of contents for the ZIP file.''' fp = self.fp try: endrec = _EndRecData(fp) except OSError: - raise BadZipfile("File is not a zip file") + raise BadZipfile('File is not a zip file') if not endrec: - raise BadZipfile("File is not a zip file") + raise BadZipfile('File is not a zip file') if self.debug > 1: print(endrec) size_cd = endrec[_ECD_SIZE] # bytes in central directory @@ -836,7 +836,7 @@ class ZipFile: if self.debug > 2: inferred = concat + offset_cd - print("given, inferred, offset", offset_cd, inferred, concat) + print('given, inferred, offset', offset_cd, inferred, concat) # self.start_dir: Position of start of central directory self.start_dir = offset_cd + concat fp.seek(self.start_dir, 0) @@ -846,7 +846,7 @@ class ZipFile: while total < size_cd: centdir = fp.read(sizeCentralDir) if centdir[0:4] != stringCentralDir: - raise BadZipfile("Bad magic number for central directory") + raise BadZipfile('Bad magic number for central directory') centdir = struct.unpack(structCentralDir, centdir) if self.debug > 2: print(centdir) @@ -878,14 +878,14 @@ class ZipFile: centdir[_CD_COMMENT_LENGTH]) if self.debug > 2: - print("total", total) + print('total', total) def _calculate_file_offsets(self): for zip_info in self.filelist: self.fp.seek(zip_info.header_offset, 0) fheader = self.fp.read(30) if fheader[0:4] != stringFileHeader: - raise BadZipfile("Bad magic number for file header") + raise BadZipfile('Bad magic number for file header') fheader = struct.unpack(structFileHeader, fheader) # file_offset is computed here, since the extra field for # the central directory and for the local file header @@ -904,8 +904,8 @@ class ZipFile: zip_info.file_offset = file_offset def replace(self, filename, arcname=None, compress_type=None): - """Delete arcname, and put the bytes from filename into the - archive under the name arcname.""" + '''Delete arcname, and put the bytes from filename into the + archive under the name arcname.''' deleteName = arcname if deleteName is None: deleteName = filename @@ -919,12 +919,12 @@ class ZipFile: self.writestr(zinfo, byts) def delete(self, name): - """Delete the file from the archive. If it appears multiple - times only the first instance will be deleted.""" + '''Delete the file from the archive. If it appears multiple + times only the first instance will be deleted.''' for i in range(0, len(self.filelist)): if self.filelist[i].filename == name: if self.debug: - print("Removing", name) + print('Removing', name) deleted_offset = self.filelist[i].header_offset deleted_size = (self.filelist[i].file_offset - self.filelist[i].header_offset) + self.filelist[i].compress_size zinfo_size = struct.calcsize(structCentralDir) + len(self.filelist[i].filename) + len(self.filelist[i].extra) @@ -953,35 +953,35 @@ class ZipFile: self._didModify = True return if self.debug: - print(name, "not in archive") + print(name, 'not in archive') def namelist(self): - """Return a list of file names in the archive.""" + '''Return a list of file names in the archive.''' l = [] for data in self.filelist: l.append(data.filename) return l def infolist(self): - """Return a list of class ZipInfo instances for files in the - archive.""" + '''Return a list of class ZipInfo instances for files in the + archive.''' return self.filelist def printdir(self): - """Print a table of contents for the zip file.""" - print("%-46s %19s %12s" % ("File Name", "Modified ", "Size")) + '''Print a table of contents for the zip file.''' + print('%-46s %19s %12s' % ('File Name', 'Modified ', 'Size')) for zinfo in self.filelist: - date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time[:6] - print("%-46s %s %12d" % (zinfo.filename, date, zinfo.file_size)) + date = '%d-%02d-%02d %02d:%02d:%02d' % zinfo.date_time[:6] + print('%-46s %s %12d' % (zinfo.filename, date, zinfo.file_size)) def testzip(self): - """Read all the files and check the CRC.""" + '''Read all the files and check the CRC.''' chunk_size = 2 ** 20 for zinfo in self.filelist: try: # Read by chunks, to avoid an OverflowError or a # MemoryError with very large embedded files. - f = self.open(zinfo.filename, "r") + f = self.open(zinfo.filename, 'r') while f.read(chunk_size): # Check CRC-32 pass except BadZipfile: @@ -997,25 +997,25 @@ class ZipFile: return info def setpassword(self, pwd): - """Set default password for encrypted files.""" + '''Set default password for encrypted files.''' self.pwd = pwd def read(self, name, pwd=None): - """Return file bytes (as a string) for name.""" - return self.open(name, "r", pwd).read() + '''Return file bytes (as a string) for name.''' + return self.open(name, 'r', pwd).read() - def read_raw(self, name, mode="r", pwd=None): - """Return the raw bytes in the zipfile corresponding to name.""" + def read_raw(self, name, mode='r', pwd=None): + '''Return the raw bytes in the zipfile corresponding to name.''' zef = self.open(name, mode=mode, pwd=pwd) return zef.read_raw() - def open(self, name, mode="r", pwd=None): + def open(self, name, mode='r', pwd=None): """Return file-like object for 'name'.""" - if mode not in ("r", "U", "rU"): + if mode not in ('r', 'U', 'rU'): raise RuntimeError('open() requires mode "r", "U", or "rU"') if not self.fp: raise RuntimeError( - "Attempt to read ZIP archive that was already closed") + 'Attempt to read ZIP archive that was already closed') # Only open a new file for instances where we were not # given a file object in the constructor @@ -1037,7 +1037,7 @@ class ZipFile: # Skip the file header: fheader = zef_file.read(sizeFileHeader) if fheader[0:4] != stringFileHeader: - raise BadZipfile("Bad magic number for file header") + raise BadZipfile('Bad magic number for file header') fheader = struct.unpack(structFileHeader, fheader) fname = zef_file.read(fheader[_FH_FILENAME_LENGTH]) @@ -1060,8 +1060,8 @@ class ZipFile: if not pwd: pwd = self.pwd if not pwd: - raise RuntimeError(("File %s is encrypted, " - "password required for extraction") % name) + raise RuntimeError(('File %s is encrypted, ' + 'password required for extraction') % name) zd = _ZipDecrypter(pwd) # The first 12 bytes in the cypher stream is an encryption header @@ -1078,7 +1078,7 @@ class ZipFile: # compare against the CRC otherwise check_byte = (zinfo.CRC >> 24) & 0xff if h[11] != check_byte: - raise RuntimeError("Bad password for file", name) + raise RuntimeError('Bad password for file', name) return ZipExtFile(zef_file, mode, zinfo, zd) @@ -1184,34 +1184,34 @@ class ZipFile: return targetpath def _writecheck(self, zinfo): - """Check for errors before writing a file to the archive.""" + '''Check for errors before writing a file to the archive.''' if zinfo.filename in self.NameToInfo: if self.debug: # Warning for duplicate names - print("Duplicate name:", zinfo.filename) - if self.mode not in ("w", "a"): + print('Duplicate name:', zinfo.filename) + if self.mode not in ('w', 'a'): raise RuntimeError('write() requires mode "w" or "a"') if not self.fp: raise RuntimeError( - "Attempt to write ZIP archive that was already closed") + 'Attempt to write ZIP archive that was already closed') if zinfo.compress_type == ZIP_DEFLATED and not zlib: raise RuntimeError( - "Compression requires the (missing) zlib module") + 'Compression requires the (missing) zlib module') if zinfo.compress_type not in (ZIP_STORED, ZIP_DEFLATED): raise RuntimeError( - "The compression method %s is not supported" % zinfo.compress_type) + 'The compression method %s is not supported' % zinfo.compress_type) if zinfo.file_size > ZIP64_LIMIT: if not self._allowZip64: - raise LargeZipFile("Filesize would require ZIP64 extensions") + raise LargeZipFile('Filesize would require ZIP64 extensions') if zinfo.header_offset > ZIP64_LIMIT: if not self._allowZip64: - raise LargeZipFile("Zipfile size would require ZIP64 extensions") + raise LargeZipFile('Zipfile size would require ZIP64 extensions') def write(self, filename, arcname=None, compress_type=None): - """Put the bytes from filename into the archive under the name - arcname.""" + '''Put the bytes from filename into the archive under the name + arcname.''' if not self.fp: raise RuntimeError( - "Attempt to write to ZIP archive that was already closed") + 'Attempt to write to ZIP archive that was already closed') st = os.stat(filename) isdir = stat.S_ISDIR(st.st_mode) @@ -1252,7 +1252,7 @@ class ZipFile: self.fp.write(zinfo.FileHeader()) return - with open(filename, "rb") as fp: + with open(filename, 'rb') as fp: # Must overwrite CRC and sizes with correct data later zinfo.CRC = CRC = 0 zinfo.compress_size = compress_size = 0 @@ -1285,7 +1285,7 @@ class ZipFile: # Seek backwards and write CRC and file sizes position = self.fp.tell() # Preserve current position in file self.fp.seek(zinfo.header_offset + 14, 0) - self.fp.write(struct.pack("<LLL", zinfo.CRC, zinfo.compress_size, + self.fp.write(struct.pack('<LLL', zinfo.CRC, zinfo.compress_size, zinfo.file_size)) self.fp.seek(position, 0) self.filelist.append(zinfo) @@ -1312,7 +1312,7 @@ class ZipFile: if not self.fp: raise RuntimeError( - "Attempt to write to ZIP archive that was already closed") + 'Attempt to write to ZIP archive that was already closed') if not raw_bytes: zinfo.file_size = len(byts) # Uncompressed size @@ -1334,7 +1334,7 @@ class ZipFile: self.fp.flush() if zinfo.flag_bits & 0x08: # Write CRC and file sizes after the file data - self.fp.write(struct.pack("<LLL", zinfo.CRC, zinfo.compress_size, + self.fp.write(struct.pack('<LLL', zinfo.CRC, zinfo.compress_size, zinfo.file_size)) self.filelist.append(zinfo) self.NameToInfo[zinfo.filename] = zinfo @@ -1357,16 +1357,16 @@ class ZipFile: self.write(f, arcname) def __del__(self): - """Call the "close()" method in case the user forgot.""" + '''Call the "close()" method in case the user forgot.''' self.close() def close(self): - """Close the file, and for mode "w" and "a" write the ending - records.""" + '''Close the file, and for mode "w" and "a" write the ending + records.''' if self.fp is None: return - if self.mode in ("w", "a") and self._didModify: # write ending records + if self.mode in ('w', 'a') and self._didModify: # write ending records count = 0 pos1 = self.fp.tell() for zinfo in self.filelist: # write central directory @@ -1526,10 +1526,10 @@ def safe_replace(zipstream, name, datastream, extra_replacements={}, class PyZipFile(ZipFile): - """Class to create ZIP archives with Python library files and packages.""" + '''Class to create ZIP archives with Python library files and packages.''' - def writepy(self, pathname, basename=""): - """Add all files from "pathname" to the ZIP archive. + def writepy(self, pathname, basename=''): + '''Add all files from "pathname" to the ZIP archive. If pathname is a package directory, search the directory and all package subdirectories recursively for all *.py and enter @@ -1539,70 +1539,70 @@ class PyZipFile(ZipFile): archive. Added modules are always module.pyo or module.pyc. This method will compile the module.py into module.pyc if necessary. - """ + ''' dir, name = os.path.split(pathname) if os.path.isdir(pathname): - initname = os.path.join(pathname, "__init__.py") + initname = os.path.join(pathname, '__init__.py') if os.path.isfile(initname): # This is a package directory, add it if basename: - basename = f"{basename}/{name}" + basename = f'{basename}/{name}' else: basename = name if self.debug: - print("Adding package in", pathname, "as", basename) + print('Adding package in', pathname, 'as', basename) fname, arcname = self._get_codename(initname[0:-3], basename) if self.debug: - print("Adding", arcname) + print('Adding', arcname) self.write(fname, arcname) dirlist = os.listdir(pathname) - dirlist.remove("__init__.py") + dirlist.remove('__init__.py') # Add all *.py files and package subdirectories for filename in dirlist: path = os.path.join(pathname, filename) ext = os.path.splitext(filename)[-1] if os.path.isdir(path): - if os.path.isfile(os.path.join(path, "__init__.py")): + if os.path.isfile(os.path.join(path, '__init__.py')): # This is a package directory, add it self.writepy(path, basename) # Recursive call - elif ext == ".py": + elif ext == '.py': fname, arcname = self._get_codename(path[0:-3], basename) if self.debug: - print("Adding", arcname) + print('Adding', arcname) self.write(fname, arcname) else: # This is NOT a package directory, add its files at top level if self.debug: - print("Adding files from directory", pathname) + print('Adding files from directory', pathname) for filename in os.listdir(pathname): path = os.path.join(pathname, filename) ext = os.path.splitext(filename)[-1] - if ext == ".py": + if ext == '.py': fname, arcname = self._get_codename(path[0:-3], basename) if self.debug: - print("Adding", arcname) + print('Adding', arcname) self.write(fname, arcname) else: - if pathname[-3:] != ".py": + if pathname[-3:] != '.py': raise RuntimeError( 'Files added with writepy() must end with ".py"') fname, arcname = self._get_codename(pathname[0:-3], basename) if self.debug: - print("Adding file", arcname) + print('Adding file', arcname) self.write(fname, arcname) def _get_codename(self, pathname, basename): - """Return (filename, archivename) for the path. + '''Return (filename, archivename) for the path. Given a module name path, return the correct file path and archive name, compiling if necessary. For example, given /python/lib/string, return (/python/lib/string.pyc, string). - """ - file_py = pathname + ".py" - file_pyc = pathname + ".pyc" - file_pyo = pathname + ".pyo" + ''' + file_py = pathname + '.py' + file_pyc = pathname + '.pyc' + file_pyo = pathname + '.pyo' if os.path.isfile(file_pyo) and \ os.stat(file_pyo).st_mtime >= os.stat(file_py).st_mtime: fname = file_pyo # Use .pyo file @@ -1610,7 +1610,7 @@ class PyZipFile(ZipFile): os.stat(file_pyc).st_mtime < os.stat(file_py).st_mtime: import py_compile if self.debug: - print("Compiling", file_py) + print('Compiling', file_py) try: py_compile.compile(file_py, file_pyc, None, True) except py_compile.PyCompileError as err: @@ -1620,7 +1620,7 @@ class PyZipFile(ZipFile): fname = file_pyc archivename = os.path.split(fname)[1] if basename: - archivename = f"{basename}/{archivename}" + archivename = f'{basename}/{archivename}' return (fname, archivename) @@ -1631,13 +1631,13 @@ def extractall(source, dest): def main(args=None): import textwrap - USAGE=textwrap.dedent("""\ + USAGE=textwrap.dedent('''\ Usage: zipfile.py -l zipfile.zip # Show listing of a zipfile zipfile.py -t zipfile.zip # Test if a zipfile is valid zipfile.py -e zipfile.zip target # Extract zipfile into target dir zipfile.py -c zipfile.zip src ... # Create zipfile from sources - """) + ''') if args is None: args = sys.argv[1:] @@ -1660,8 +1660,8 @@ def main(args=None): zf = ZipFile(args[1], 'r') badfile = zf.testzip() if badfile: - print(f"The following enclosed file is corrupted: {badfile!r}") - print("Done testing") + print(f'The following enclosed file is corrupted: {badfile!r}') + print('Done testing') elif args[0] == '-e': if len(args) != 3: @@ -1704,5 +1704,5 @@ def main(args=None): zf.close() -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/src/calibre/web/__init__.py b/src/calibre/web/__init__.py index 50c0b76adf..03905d2ffd 100644 --- a/src/calibre/web/__init__.py +++ b/src/calibre/web/__init__.py @@ -19,12 +19,12 @@ def get_download_filename_from_response(response): if 'filename' in p: if '*=' in disposition: parts = disposition.split('*=')[-1] - filename = parts.split('\'')[-1] + filename = parts.split("'")[-1] else: filename = disposition.split('=')[-1] - if filename[0] in ('\'', '"'): + if filename[0] in ("'", '"'): filename = filename[1:] - if filename[-1] in ('\'', '"'): + if filename[-1] in ("'", '"'): filename = filename[:-1] filename = unquote(filename) break diff --git a/src/calibre/web/feeds/__init__.py b/src/calibre/web/feeds/__init__.py index 5d2cad1846..51d59995a8 100644 --- a/src/calibre/web/feeds/__init__.py +++ b/src/calibre/web/feeds/__init__.py @@ -65,7 +65,7 @@ class Article: def formatted_date(self): if self._formatted_date is None: - self._formatted_date = strftime(" [%a, %d %b %H:%M]", + self._formatted_date = strftime(' [%a, %d %b %H:%M]', t=self.localtime.timetuple()) return self._formatted_date diff --git a/src/calibre/web/feeds/news.py b/src/calibre/web/feeds/news.py index 6a732745ac..7ebfead4d5 100644 --- a/src/calibre/web/feeds/news.py +++ b/src/calibre/web/feeds/news.py @@ -3,7 +3,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' ''' Defines various abstract base classes that can be subclassed to create powerful news fetching recipes. ''' -__docformat__ = "restructuredtext en" +__docformat__ = 'restructuredtext en' import io @@ -1159,7 +1159,7 @@ class BasicNewsRecipe(Recipe): templ = templ(lang=self.lang_for_html) css = self.template_css + '\n\n' +(self.get_extra_css() or '') timefmt = self.timefmt - return templ.generate(self.title, "mastheadImage.jpg", timefmt, feeds, + return templ.generate(self.title, 'mastheadImage.jpg', timefmt, feeds, extra_css=css).render(doctype='xhtml') @classmethod @@ -1478,7 +1478,7 @@ class BasicNewsRecipe(Recipe): try: self._download_masthead(url) except: - self.log.exception("Failed to download supplied masthead_url") + self.log.exception('Failed to download supplied masthead_url') def resolve_masthead(self): self.masthead_path = None @@ -1493,7 +1493,7 @@ class BasicNewsRecipe(Recipe): # Failure sets self.masthead_path to None self.download_masthead(murl) if self.masthead_path is None: - self.log.info("Synthesizing mastheadImage") + self.log.info('Synthesizing mastheadImage') self.masthead_path = os.path.join(self.output_dir, 'mastheadImage.jpg') try: self.default_masthead_image(self.masthead_path) diff --git a/src/calibre/web/feeds/templates.py b/src/calibre/web/feeds/templates.py index 524ae9222b..e546703d07 100644 --- a/src/calibre/web/feeds/templates.py +++ b/src/calibre/web/feeds/templates.py @@ -106,7 +106,7 @@ class IndexTemplate(Template): href='feed_%d/index.html'%i)), id='feed_%d'%i) ul.append(li) div = DIV( - PT(IMG(src=masthead,alt="masthead"),style='text-align:center'), + PT(IMG(src=masthead,alt='masthead'),style='text-align:center'), PT(date, style='text-align:right'), ul, attrs(rescale=100)) @@ -132,7 +132,7 @@ class FeedTemplate(Template): link = A(_('Next section'), href='../feed_%d/index.html'%(f+1)) link.tail = ' | ' navbar.append(link) - link = A(_('Main menu'), href="../index.html") + link = A(_('Main menu'), href='../index.html') link.tail = ' | ' navbar.append(link) if f > 0: @@ -249,11 +249,11 @@ class TouchscreenIndexTemplate(Template): def _generate(self, title, masthead, datefmt, feeds, extra_css=None, style=None): self.IS_HTML = False date = '{}, {} {}, {}'.format(strftime('%A'), strftime('%B'), strftime('%d').lstrip('0'), strftime('%Y')) - masthead_p = etree.Element("p") - masthead_p.set("style","text-align:center") - masthead_img = etree.Element("img") - masthead_img.set("src",masthead) - masthead_img.set("alt","masthead") + masthead_p = etree.Element('p') + masthead_p.set('style','text-align:center') + masthead_img = etree.Element('img') + masthead_img.set('src',masthead) + masthead_img.set('alt','masthead') masthead_p.append(masthead_img) head = HEAD(TITLE(title)) @@ -262,12 +262,12 @@ class TouchscreenIndexTemplate(Template): if extra_css: head.append(STYLE(extra_css, type='text/css')) - toc = TABLE(attrs('toc'),width="100%",border="0",cellpadding="3px") + toc = TABLE(attrs('toc'),width='100%',border='0',cellpadding='3px') for i, feed in enumerate(feeds): if len(feed): tr = TR() tr.append(TD(attrs(rescale=120), A(feed.title, href='feed_%d/index.html'%i))) - tr.append(TD('%s' % len(feed.articles), style="text-align:right")) + tr.append(TD('%s' % len(feed.articles), style='text-align:right')) toc.append(tr) div = DIV( masthead_p, @@ -317,7 +317,7 @@ class TouchscreenFeedTemplate(Template): navbar_tr.append(TD(attrs('feed_prev'),link)) # Up to Sections - link = A(_('Sections'), href="../index.html") + link = A(_('Sections'), href='../index.html') navbar_tr.append(TD(attrs('feed_up'),link)) # Next Section diff --git a/src/odf/attrconverters.py b/src/odf/attrconverters.py index 1687bff05f..f27c18f199 100644 --- a/src/odf/attrconverters.py +++ b/src/odf/attrconverters.py @@ -50,7 +50,7 @@ pattern_vector3D = re.compile(r'\([ ]*-?([0-9]+(\.[0-9]*)?|\.[0-9]+)([ ]+-?([0-9 def make_NCName(arg): for c in (':',' '): - arg = arg.replace(c,"_%x_" % ord(c)) + arg = arg.replace(c,'_%x_' % ord(c)) return arg @@ -59,31 +59,31 @@ def cnv_anyURI(attribute, arg, element): def cnv_boolean(attribute, arg, element): - if arg.lower() in ("false","no"): - return "false" + if arg.lower() in ('false','no'): + return 'false' if arg: - return "true" - return "false" + return 'true' + return 'false' # Potentially accept color values def cnv_color(attribute, arg, element): - """ A RGB color in conformance with §5.9.11 of [XSL], that is a RGB color in notation “#rrggbb”, where + ''' A RGB color in conformance with §5.9.11 of [XSL], that is a RGB color in notation “#rrggbb”, where rr, gg and bb are 8-bit hexadecimal digits. - """ + ''' return unicode_type(arg) def cnv_configtype(attribute, arg, element): - if unicode_type(arg) not in ("boolean", "short", "int", "long", - "double", "string", "datetime", "base64Binary"): + if unicode_type(arg) not in ('boolean', 'short', 'int', 'long', + 'double', 'string', 'datetime', 'base64Binary'): raise ValueError("'%s' not allowed" % unicode_type(arg)) return unicode_type(arg) def cnv_data_source_has_labels(attribute, arg, element): - if unicode_type(arg) not in ("none","row","column","both"): + if unicode_type(arg) not in ('none','row','column','both'): raise ValueError("'%s' not allowed" % unicode_type(arg)) return unicode_type(arg) @@ -91,16 +91,16 @@ def cnv_data_source_has_labels(attribute, arg, element): def cnv_date(attribute, arg, element): - """ A dateOrDateTime value is either an [xmlschema-2] date value or an [xmlschema-2] dateTime + ''' A dateOrDateTime value is either an [xmlschema-2] date value or an [xmlschema-2] dateTime value. - """ + ''' return unicode_type(arg) def cnv_dateTime(attribute, arg, element): - """ A dateOrDateTime value is either an [xmlschema-2] date value or an [xmlschema-2] dateTime + ''' A dateOrDateTime value is either an [xmlschema-2] date value or an [xmlschema-2] dateTime value. - """ + ''' return unicode_type(arg) @@ -113,9 +113,9 @@ def cnv_duration(attribute, arg, element): def cnv_family(attribute, arg, element): - """ A style family """ - if unicode_type(arg) not in ("text", "paragraph", "section", "ruby", "table", "table-column", "table-row", "table-cell", - "graphic", "presentation", "drawing-page", "chart"): + ''' A style family ''' + if unicode_type(arg) not in ('text', 'paragraph', 'section', 'ruby', 'table', 'table-column', 'table-row', 'table-cell', + 'graphic', 'presentation', 'drawing-page', 'chart'): raise ValueError("'%s' not allowed" % unicode_type(arg)) return unicode_type(arg) @@ -132,11 +132,11 @@ def __save_prefix(attribute, arg, element): def cnv_formula(attribute, arg, element): - """ A string containing a formula. Formulas do not have a predefined syntax, but the string should + ''' A string containing a formula. Formulas do not have a predefined syntax, but the string should begin with a namespace prefix, followed by a “:” (COLON, U+003A) separator, followed by the text of the formula. The namespace bound to the prefix determines the syntax and semantics of the formula. - """ + ''' return __save_prefix(attribute, arg, element) @@ -153,7 +153,7 @@ def cnv_integer(attribute, arg, element): def cnv_legend_position(attribute, arg, element): - if unicode_type(arg) not in ("start", "end", "top", "bottom", "top-start", "bottom-start", "top-end", "bottom-end"): + if unicode_type(arg) not in ('start', 'end', 'top', 'bottom', 'top-start', 'bottom-start', 'top-end', 'bottom-end'): raise ValueError("'%s' not allowed" % unicode_type(arg)) return unicode_type(arg) @@ -162,9 +162,9 @@ pattern_length = re.compile(r'-?([0-9]+(\.[0-9]*)?|\.[0-9]+)((cm)|(mm)|(in)|(pt) def cnv_length(attribute, arg, element): - """ A (positive or negative) physical length, consisting of magnitude and unit, in conformance with the + ''' A (positive or negative) physical length, consisting of magnitude and unit, in conformance with the Units of Measure defined in §5.9.13 of [XSL]. - """ + ''' global pattern_length if not pattern_length.match(arg): raise ValueError("'%s' is not a valid length" % arg) @@ -187,7 +187,7 @@ def cnv_lengthorpercent(attribute, arg, element): def cnv_metavaluetype(attribute, arg, element): - if unicode_type(arg) not in ("float", "date", "time", "boolean", "string"): + if unicode_type(arg) not in ('float', 'date', 'time', 'boolean', 'string'): raise ValueError("'%s' not allowed" % unicode_type(arg)) return unicode_type(arg) @@ -271,13 +271,13 @@ def cnv_points(attribute, arg, element): global pattern_points if isinstance(arg, string_or_bytes): if not pattern_points.match(arg): - raise ValueError("x,y are separated by a comma and the points are separated by white spaces") + raise ValueError('x,y are separated by a comma and the points are separated by white spaces') return arg else: try: - strarg = ' '.join(["%d,%d" % p for p in arg]) + strarg = ' '.join(['%d,%d' % p for p in arg]) except: - raise ValueError("Points must be string or [(0,0),(1,1)] - not %s" % arg) + raise ValueError('Points must be string or [(0,0),(1,1)] - not %s' % arg) return strarg @@ -290,7 +290,7 @@ def cnv_string(attribute, arg, element): def cnv_textnoteclass(attribute, arg, element): - if unicode_type(arg) not in ("footnote", "endnote"): + if unicode_type(arg) not in ('footnote', 'endnote'): raise ValueError("'%s' not allowed" % unicode_type(arg)) return unicode_type(arg) @@ -311,12 +311,12 @@ pattern_viewbox = re.compile(r'-?[0-9]+([ ]+-?[0-9]+){3}$') def cnv_viewbox(attribute, arg, element): global pattern_viewbox if not pattern_viewbox.match(arg): - raise ValueError("viewBox must be four integers separated by whitespaces") + raise ValueError('viewBox must be four integers separated by whitespaces') return arg def cnv_xlinkshow(attribute, arg, element): - if unicode_type(arg) not in ("new", "replace", "embed"): + if unicode_type(arg) not in ('new', 'replace', 'embed'): raise ValueError("'%s' not allowed" % unicode_type(arg)) return unicode_type(arg) @@ -1533,9 +1533,9 @@ attrconverters = { class AttrConverters: def convert(self, attribute, value, element): - """ Based on the element, figures out how to check/convert the attribute value + ''' Based on the element, figures out how to check/convert the attribute value All values are converted to string - """ + ''' conversion = attrconverters.get((attribute, element.qname), None) if conversion is not None: return conversion(attribute, value, element) diff --git a/src/odf/easyliststyle.py b/src/odf/easyliststyle.py index 11a2b8de05..42f4097058 100644 --- a/src/odf/easyliststyle.py +++ b/src/odf/easyliststyle.py @@ -26,7 +26,7 @@ from polyglot.builtins import unicode_type from .style import ListLevelProperties from .text import ListLevelStyleBullet, ListLevelStyleNumber, ListStyle -""" +''' Create a <text:list-style> element from a string or array. List styles require a lot of code to create one level at a time. @@ -38,7 +38,7 @@ Each item in the string (or array) represents a list level * <p>If an item contains <code>1</code>, <code>I</code>, * <code>i</code>, <code>A</code>, or <code>a</code>, then it is presumed * to be a numbering style; otherwise it is a bulleted style.</p> -""" +''' _MAX_LIST_LEVEL = 10 SHOW_ALL_LEVELS = True @@ -51,16 +51,16 @@ def styleFromString(name, specifiers, delim, spacing, showAllLevels): def styleFromList(styleName, specArray, spacing, showAllLevels): - bullet = "" - numPrefix = "" - numSuffix = "" + bullet = '' + numPrefix = '' + numSuffix = '' cssLengthNum = 0 - cssLengthUnits = "" + cssLengthUnits = '' numbered = False displayLevels = 0 listStyle = ListStyle(name=styleName) - numFormatPattern = re.compile("([1IiAa])") - cssLengthPattern = re.compile("([^a-z]+)\\s*([a-z]+)?") + numFormatPattern = re.compile('([1IiAa])') + cssLengthPattern = re.compile('([^a-z]+)\\s*([a-z]+)?') m = cssLengthPattern.search(spacing) if (m is not None): cssLengthNum = float(m.group(1)) @@ -73,7 +73,7 @@ def styleFromList(styleName, specArray, spacing, showAllLevels): if (m is not None): numPrefix = specification[0:m.start(1)] numSuffix = specification[m.end(1):] - bullet = "" + bullet = '' numbered = True if (showAllLevels): displayLevels = i + 1 @@ -81,8 +81,8 @@ def styleFromList(styleName, specArray, spacing, showAllLevels): displayLevels = 1 else: # it's a bullet style bullet = specification - numPrefix = "" - numSuffix = "" + numPrefix = '' + numSuffix = '' displayLevels = 1 numbered = False if (numbered): diff --git a/src/odf/element.py b/src/odf/element.py index 99b77e83a7..740cbff9a9 100644 --- a/src/odf/element.py +++ b/src/odf/element.py @@ -37,22 +37,22 @@ from .namespaces import nsdict def _escape(data, entities={}): - """ Escape &, <, and > in a string of data. + ''' Escape &, <, and > in a string of data. You can escape other strings of data by passing a dictionary as the optional entities parameter. The keys and values must all be strings; each key will be replaced with its corresponding value. - """ - data = data.replace("&", "&") - data = data.replace("<", "<") - data = data.replace(">", ">") + ''' + data = data.replace('&', '&') + data = data.replace('<', '<') + data = data.replace('>', '>') for chars, entity in entities.items(): data = data.replace(chars, entity) return data def _quoteattr(data, entities={}): - """ Escape and quote an attribute value. + ''' Escape and quote an attribute value. Escape &, <, and > in a string of data, then quote it for use as an attribute value. The \" character will be escaped as well, if @@ -61,13 +61,13 @@ def _quoteattr(data, entities={}): You can escape other strings of data by passing a dictionary as the optional entities parameter. The keys and values must all be strings; each key will be replaced with its corresponding value. - """ + ''' entities['\n']=' ' entities['\r']=' ' data = _escape(data, entities) if '"' in data: if "'" in data: - data = '"%s"' % data.replace('"', """) + data = '"%s"' % data.replace('"', '"') else: data = "'%s'" % data else: @@ -76,7 +76,7 @@ def _quoteattr(data, entities={}): def _nssplit(qualifiedName): - """ Split a qualified name into namespace part and local part. """ + ''' Split a qualified name into namespace part and local part. ''' fields = qualifiedName.split(':', 1) if len(fields) == 2: return fields @@ -85,29 +85,29 @@ def _nssplit(qualifiedName): def _nsassign(namespace): - return nsdict.setdefault(namespace,"ns" + unicode_type(len(nsdict))) + return nsdict.setdefault(namespace,'ns' + unicode_type(len(nsdict))) # Exceptions class IllegalChild(Exception): - """ Complains if you add an element to a parent where it is not allowed """ + ''' Complains if you add an element to a parent where it is not allowed ''' class IllegalText(Exception): - """ Complains if you add text or cdata to an element where it is not allowed """ + ''' Complains if you add text or cdata to an element where it is not allowed ''' class Node(xml.dom.Node): - """ super class for more specific nodes """ + ''' super class for more specific nodes ''' parentNode = None nextSibling = None previousSibling = None def hasChildNodes(self): - """ Tells whether this element has any children; text nodes, + ''' Tells whether this element has any children; text nodes, subelements, whatever. - """ + ''' if self.childNodes: return True else: @@ -125,11 +125,11 @@ class Node(xml.dom.Node): return self.childNodes[-1] def insertBefore(self, newChild, refChild): - """ Inserts the node newChild before the existing child node refChild. + ''' Inserts the node newChild before the existing child node refChild. If refChild is null, insert newChild at the end of the list of children. - """ + ''' if newChild.nodeType not in self._child_node_types: - raise IllegalChild(f"{newChild.tagName} cannot be child of {self.tagName}") + raise IllegalChild(f'{newChild.tagName} cannot be child of {self.tagName}') if newChild.parentNode is not None: newChild.parentNode.removeChild(newChild) if refChild is None: @@ -152,16 +152,16 @@ class Node(xml.dom.Node): return newChild def appendChild(self, newChild): - """ Adds the node newChild to the end of the list of children of this node. + ''' Adds the node newChild to the end of the list of children of this node. If the newChild is already in the tree, it is first removed. - """ + ''' if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE: for c in tuple(newChild.childNodes): self.appendChild(c) # The DOM does not clearly specify what to return in this case return newChild if newChild.nodeType not in self._child_node_types: - raise IllegalChild(f"<{newChild.tagName}> is not allowed in {self.tagName}") + raise IllegalChild(f'<{newChild.tagName}> is not allowed in {self.tagName}') if newChild.parentNode is not None: newChild.parentNode.removeChild(newChild) _append_child(self, newChild) @@ -169,8 +169,8 @@ class Node(xml.dom.Node): return newChild def removeChild(self, oldChild): - """ Removes the child node indicated by oldChild from the list of children, and returns it. - """ + ''' Removes the child node indicated by oldChild from the list of children, and returns it. + ''' # FIXME: update ownerDocument.element_dict or find other solution try: self.childNodes.remove(oldChild) @@ -194,8 +194,8 @@ class Node(xml.dom.Node): __str__ = __unicode__ -defproperty(Node, "firstChild", doc="First child node, or None.") -defproperty(Node, "lastChild", doc="Last child node, or None.") +defproperty(Node, 'firstChild', doc='First child node, or None.') +defproperty(Node, 'lastChild', doc='Last child node, or None.') def _append_child(self, node): @@ -203,16 +203,16 @@ def _append_child(self, node): childNodes = self.childNodes if childNodes: last = childNodes[-1] - node.__dict__["previousSibling"] = last - last.__dict__["nextSibling"] = node + node.__dict__['previousSibling'] = last + last.__dict__['nextSibling'] = node childNodes.append(node) - node.__dict__["parentNode"] = self + node.__dict__['parentNode'] = self class Childless: - """ Mixin that makes childless-ness easy to implement and avoids + ''' Mixin that makes childless-ness easy to implement and avoids the complexity of the Node methods that deal with children. - """ + ''' attributes = None childNodes = EmptyNodeList() @@ -226,32 +226,32 @@ class Childless: return None def appendChild(self, node): - """ Raises an error """ + ''' Raises an error ''' raise xml.dom.HierarchyRequestErr( - self.tagName + " nodes cannot have children") + self.tagName + ' nodes cannot have children') def hasChildNodes(self): return False def insertBefore(self, newChild, refChild): - """ Raises an error """ + ''' Raises an error ''' raise xml.dom.HierarchyRequestErr( - self.tagName + " nodes do not have children") + self.tagName + ' nodes do not have children') def removeChild(self, oldChild): - """ Raises an error """ + ''' Raises an error ''' raise xml.dom.NotFoundErr( - self.tagName + " nodes do not have children") + self.tagName + ' nodes do not have children') def replaceChild(self, newChild, oldChild): - """ Raises an error """ + ''' Raises an error ''' raise xml.dom.HierarchyRequestErr( - self.tagName + " nodes do not have children") + self.tagName + ' nodes do not have children') class Text(Childless, Node): nodeType = Node.TEXT_NODE - tagName = "Text" + tagName = 'Text' def __init__(self, data): self.data = data @@ -261,7 +261,7 @@ class Text(Childless, Node): __unicode__ = __str__ def toXml(self,level,f): - """ Write XML in UTF-8 """ + ''' Write XML in UTF-8 ''' if self.data: f.write(_escape(str(self.data).encode('utf-8'))) @@ -270,10 +270,10 @@ class CDATASection(Text, Childless): nodeType = Node.CDATA_SECTION_NODE def toXml(self,level,f): - """ Generate XML output of the node. If the text contains "]]>", then + ''' Generate XML output of the node. If the text contains "]]>", then escape it by going out of CDATA mode (]]>), then write the string and then go into CDATA mode again. (<![CDATA[) - """ + ''' if self.data: f.write('<![CDATA[%s]]>' % self.data.replace(']]>',']]>]]><![CDATA[')) @@ -304,7 +304,7 @@ class Element(Node): self.childNodes=[] self.allowed_children = grammar.allowed_children.get(self.qname) prefix = self.get_nsprefix(self.qname[0]) - self.tagName = prefix + ":" + self.qname[1] + self.tagName = prefix + ':' + self.qname[1] if text is not None: self.addText(text) if cdata is not None: @@ -334,12 +334,12 @@ class Element(Node): if required: for r in required: if self.getAttrNS(r[0],r[1]) is None: - raise AttributeError("Required attribute missing: {} in <{}>".format(r[1].lower().replace('-',''), self.tagName)) + raise AttributeError('Required attribute missing: {} in <{}>'.format(r[1].lower().replace('-',''), self.tagName)) def get_knownns(self, prefix): - """ Odfpy maintains a list of known namespaces. In some cases a prefix is used, and + ''' Odfpy maintains a list of known namespaces. In some cases a prefix is used, and we need to know which namespace it resolves to. - """ + ''' global nsdict for ns,p in nsdict.items(): if p == prefix: @@ -347,11 +347,11 @@ class Element(Node): return None def get_nsprefix(self, namespace): - """ Odfpy maintains a list of known namespaces. In some cases we have a namespace URL, + ''' Odfpy maintains a list of known namespaces. In some cases we have a namespace URL, and needs to look up or assign the prefix for it. - """ + ''' if namespace is None: - namespace = "" + namespace = '' prefix = _nsassign(namespace) if namespace not in self.namespaces: self.namespaces[namespace] = prefix @@ -366,74 +366,74 @@ class Element(Node): self._setOwnerDoc(child) def addElement(self, element, check_grammar=True): - """ adds an element to an Element + ''' adds an element to an Element Element.addElement(Element) - """ + ''' if check_grammar and self.allowed_children is not None: if element.qname not in self.allowed_children: - raise IllegalChild(f"<{element.tagName}> is not allowed in <{self.tagName}>") + raise IllegalChild(f'<{element.tagName}> is not allowed in <{self.tagName}>') self.appendChild(element) self._setOwnerDoc(element) if self.ownerDocument: self.ownerDocument.rebuild_caches(element) def addText(self, text, check_grammar=True): - """ Adds text to an element + ''' Adds text to an element Setting check_grammar=False turns off grammar checking - """ + ''' if check_grammar and self.qname not in grammar.allows_text: - raise IllegalText("The <%s> element does not allow text" % self.tagName) + raise IllegalText('The <%s> element does not allow text' % self.tagName) else: if text != '': self.appendChild(Text(text)) def addCDATA(self, cdata, check_grammar=True): - """ Adds CDATA to an element + ''' Adds CDATA to an element Setting check_grammar=False turns off grammar checking - """ + ''' if check_grammar and self.qname not in grammar.allows_text: - raise IllegalText("The <%s> element does not allow text" % self.tagName) + raise IllegalText('The <%s> element does not allow text' % self.tagName) else: self.appendChild(CDATASection(cdata)) def removeAttribute(self, attr, check_grammar=True): - """ Removes an attribute by name. """ + ''' Removes an attribute by name. ''' allowed_attrs = self.allowed_attributes() if allowed_attrs is None: if isinstance(attr, tuple): prefix, localname = attr self.removeAttrNS(prefix, localname) else: - raise AttributeError("Unable to add simple attribute - use (namespace, localpart)") + raise AttributeError('Unable to add simple attribute - use (namespace, localpart)') else: # Construct a list of allowed arguments allowed_args = [a[1].lower().replace('-','') for a in allowed_attrs] if check_grammar and attr not in allowed_args: - raise AttributeError(f"Attribute {attr} is not allowed in <{self.tagName}>") + raise AttributeError(f'Attribute {attr} is not allowed in <{self.tagName}>') i = allowed_args.index(attr) self.removeAttrNS(allowed_attrs[i][0], allowed_attrs[i][1]) def setAttribute(self, attr, value, check_grammar=True): - """ Add an attribute to the element + ''' Add an attribute to the element This is sort of a convenience method. All attributes in ODF have namespaces. The library knows what attributes are legal and then allows the user to provide the attribute as a keyword argument and the library will add the correct namespace. Must overwrite, If attribute already exists. - """ + ''' allowed_attrs = self.allowed_attributes() if allowed_attrs is None: if isinstance(attr, tuple): prefix, localname = attr self.setAttrNS(prefix, localname, value) else: - raise AttributeError("Unable to add simple attribute - use (namespace, localpart)") + raise AttributeError('Unable to add simple attribute - use (namespace, localpart)') else: # Construct a list of allowed arguments allowed_args = [a[1].lower().replace('-','') for a in allowed_attrs] if check_grammar and attr not in allowed_args: - raise AttributeError(f"Attribute {attr} is not allowed in <{self.tagName}>") + raise AttributeError(f'Attribute {attr} is not allowed in <{self.tagName}>') i = allowed_args.index(attr) self.setAttrNS(allowed_attrs[i][0], allowed_attrs[i][1], value) @@ -454,15 +454,15 @@ class Element(Node): del self.attributes[(namespace, localpart)] def getAttribute(self, attr): - """ Get an attribute value. The method knows which namespace the attribute is in - """ + ''' Get an attribute value. The method knows which namespace the attribute is in + ''' allowed_attrs = self.allowed_attributes() if allowed_attrs is None: if isinstance(attr, tuple): prefix, localname = attr return self.getAttrNS(prefix, localname) else: - raise AttributeError("Unable to get simple attribute - use (namespace, localpart)") + raise AttributeError('Unable to get simple attribute - use (namespace, localpart)') else: # Construct a list of allowed arguments allowed_args = [a[1].lower().replace('-','') for a in allowed_attrs] @@ -483,7 +483,7 @@ class Element(Node): f.write('</'+self.tagName+'>') def toXml(self, level, f): - """ Generate XML stream out of the tree structure """ + ''' Generate XML stream out of the tree structure ''' f.write('<'+self.tagName) if level == 0: for namespace, prefix in self.namespaces.items(): @@ -508,11 +508,11 @@ class Element(Node): return accumulator def getElementsByType(self, element): - """ Gets elements based on the type, which is function from text.py, draw.py etc. """ + ''' Gets elements based on the type, which is function from text.py, draw.py etc. ''' obj = element(check_grammar=False) return self._getElementsByObj(obj,[]) def isInstanceOf(self, element): - """ This is a check to see if the object is an instance of a type """ + ''' This is a check to see if the object is an instance of a type ''' obj = element(check_grammar=False) return self.qname == obj.qname diff --git a/src/odf/grammar.py b/src/odf/grammar.py index 44584a5f76..2db6b3abad 100644 --- a/src/odf/grammar.py +++ b/src/odf/grammar.py @@ -18,10 +18,10 @@ # -__doc__=""" In principle the OpenDocument schema converted to python structures. +__doc__=''' In principle the OpenDocument schema converted to python structures. Currently it contains the legal child elements of a given element. To be used for validation check in the API -""" +''' from .namespaces import ( ANIMNS, diff --git a/src/odf/load.py b/src/odf/load.py index 42b3e1793a..14ef11f861 100644 --- a/src/odf/load.py +++ b/src/odf/load.py @@ -35,7 +35,7 @@ from .namespaces import OFFICENS class LoadParser(handler.ContentHandler): - """ Extract headings from content.xml of an ODT file """ + ''' Extract headings from content.xml of an ODT file ''' triggers = ( (OFFICENS, 'automatic-styles'), (OFFICENS, 'body'), (OFFICENS, 'font-face-decls'), (OFFICENS, 'master-styles'), @@ -56,7 +56,7 @@ class LoadParser(handler.ContentHandler): def startElementNS(self, tag, qname, attrs): if tag in self.triggers: self.parse = True - if self.doc._parsing != "styles.xml" and tag == (OFFICENS, 'font-face-decls'): + if self.doc._parsing != 'styles.xml' and tag == (OFFICENS, 'font-face-decls'): self.parse = False if self.parse is False: return @@ -75,7 +75,7 @@ class LoadParser(handler.ContentHandler): e = Element(qname=tag, qattributes=attrdict, check_grammar=False) self.curr = e except AttributeError as v: - print("Error: %s" % v) + print('Error: %s' % v) if tag == (OFFICENS, 'automatic-styles'): e = self.doc.automaticstyles @@ -91,7 +91,7 @@ class LoadParser(handler.ContentHandler): e = self.doc.settings elif tag == (OFFICENS,'styles'): e = self.doc.styles - elif self.doc._parsing == "styles.xml" and tag == (OFFICENS, 'font-face-decls'): + elif self.doc._parsing == 'styles.xml' and tag == (OFFICENS, 'font-face-decls'): e = self.doc.fontfacedecls elif hasattr(self,'parent'): self.parent.addElement(e, check_grammar=False) diff --git a/src/odf/namespaces.py b/src/odf/namespaces.py index 1c78099b36..df1ea676a2 100644 --- a/src/odf/namespaces.py +++ b/src/odf/namespaces.py @@ -17,50 +17,50 @@ # Contributor(s): # -TOOLSVERSION = "ODFPY/0.9.4dev" +TOOLSVERSION = 'ODFPY/0.9.4dev' -ANIMNS = "urn:oasis:names:tc:opendocument:xmlns:animation:1.0" -CHARTNS = "urn:oasis:names:tc:opendocument:xmlns:chart:1.0" -CHARTOOONS = "http://openoffice.org/2010/chart" -CONFIGNS = "urn:oasis:names:tc:opendocument:xmlns:config:1.0" -CSS3TNS = "http://www.w3.org/TR/css3-text/" +ANIMNS = 'urn:oasis:names:tc:opendocument:xmlns:animation:1.0' +CHARTNS = 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0' +CHARTOOONS = 'http://openoffice.org/2010/chart' +CONFIGNS = 'urn:oasis:names:tc:opendocument:xmlns:config:1.0' +CSS3TNS = 'http://www.w3.org/TR/css3-text/' # DBNS = u"http://openoffice.org/2004/database" -DBNS = "urn:oasis:names:tc:opendocument:xmlns:database:1.0" -DCNS = "http://purl.org/dc/elements/1.1/" -DOMNS = "http://www.w3.org/2001/xml-events" -DR3DNS = "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" -DRAWNS = "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" -FIELDNS = "urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" -FONS = "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" -FORMNS = "urn:oasis:names:tc:opendocument:xmlns:form:1.0" -FORMXNS = "urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" -GRDDLNS = "http://www.w3.org/2003/g/data-view#" -KOFFICENS = "http://www.koffice.org/2005/" -MANIFESTNS = "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" -MATHNS = "http://www.w3.org/1998/Math/MathML" -METANS = "urn:oasis:names:tc:opendocument:xmlns:meta:1.0" -NUMBERNS = "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" -OFFICENS = "urn:oasis:names:tc:opendocument:xmlns:office:1.0" -OFNS = "urn:oasis:names:tc:opendocument:xmlns:of:1.2" -OOOCNS = "http://openoffice.org/2004/calc" -OOONS = "http://openoffice.org/2004/office" -OOOWNS = "http://openoffice.org/2004/writer" -PRESENTATIONNS = "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" -RDFANS = "http://docs.oasis-open.org/opendocument/meta/rdfa#" -RPTNS = "http://openoffice.org/2005/report" -SCRIPTNS = "urn:oasis:names:tc:opendocument:xmlns:script:1.0" -SMILNS = "urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0" -STYLENS = "urn:oasis:names:tc:opendocument:xmlns:style:1.0" -SVGNS = "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" -TABLENS = "urn:oasis:names:tc:opendocument:xmlns:table:1.0" -TABLEOOONS = "http://openoffice.org/2009/table" -TEXTNS = "urn:oasis:names:tc:opendocument:xmlns:text:1.0" -XFORMSNS = "http://www.w3.org/2002/xforms" -XHTMLNS = "http://www.w3.org/1999/xhtml" -XLINKNS = "http://www.w3.org/1999/xlink" -XMLNS = "http://www.w3.org/XML/1998/namespace" -XSDNS = "http://www.w3.org/2001/XMLSchema" -XSINS = "http://www.w3.org/2001/XMLSchema-instance" +DBNS = 'urn:oasis:names:tc:opendocument:xmlns:database:1.0' +DCNS = 'http://purl.org/dc/elements/1.1/' +DOMNS = 'http://www.w3.org/2001/xml-events' +DR3DNS = 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0' +DRAWNS = 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0' +FIELDNS = 'urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0' +FONS = 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0' +FORMNS = 'urn:oasis:names:tc:opendocument:xmlns:form:1.0' +FORMXNS = 'urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0' +GRDDLNS = 'http://www.w3.org/2003/g/data-view#' +KOFFICENS = 'http://www.koffice.org/2005/' +MANIFESTNS = 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0' +MATHNS = 'http://www.w3.org/1998/Math/MathML' +METANS = 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0' +NUMBERNS = 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0' +OFFICENS = 'urn:oasis:names:tc:opendocument:xmlns:office:1.0' +OFNS = 'urn:oasis:names:tc:opendocument:xmlns:of:1.2' +OOOCNS = 'http://openoffice.org/2004/calc' +OOONS = 'http://openoffice.org/2004/office' +OOOWNS = 'http://openoffice.org/2004/writer' +PRESENTATIONNS = 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0' +RDFANS = 'http://docs.oasis-open.org/opendocument/meta/rdfa#' +RPTNS = 'http://openoffice.org/2005/report' +SCRIPTNS = 'urn:oasis:names:tc:opendocument:xmlns:script:1.0' +SMILNS = 'urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0' +STYLENS = 'urn:oasis:names:tc:opendocument:xmlns:style:1.0' +SVGNS = 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0' +TABLENS = 'urn:oasis:names:tc:opendocument:xmlns:table:1.0' +TABLEOOONS = 'http://openoffice.org/2009/table' +TEXTNS = 'urn:oasis:names:tc:opendocument:xmlns:text:1.0' +XFORMSNS = 'http://www.w3.org/2002/xforms' +XHTMLNS = 'http://www.w3.org/1999/xhtml' +XLINKNS = 'http://www.w3.org/1999/xlink' +XMLNS = 'http://www.w3.org/XML/1998/namespace' +XSDNS = 'http://www.w3.org/2001/XMLSchema' +XSINS = 'http://www.w3.org/2001/XMLSchema-instance' nsdict = { ANIMNS: 'anim', diff --git a/src/odf/odf2moinmoin.py b/src/odf/odf2moinmoin.py index 83ce1e71a0..18c86d3934 100644 --- a/src/odf/odf2moinmoin.py +++ b/src/odf/odf2moinmoin.py @@ -36,13 +36,13 @@ IGNORED_TAGS = [ 'office:annotation', 'presentation:notes', 'svg:desc', -] + [nsdict[item[0]]+":"+item[1] for item in empty_elements] +] + [nsdict[item[0]]+':'+item[1] for item in empty_elements] -INLINE_TAGS = [nsdict[item[0]]+":"+item[1] for item in inline_elements] +INLINE_TAGS = [nsdict[item[0]]+':'+item[1] for item in inline_elements] class TextProps: - """ Holds properties for a text style. """ + ''' Holds properties for a text style. ''' def __init__(self): @@ -55,26 +55,26 @@ class TextProps: self.subscript = False def setItalic(self, value): - if value == "italic": + if value == 'italic': self.italic = True - elif value == "normal": + elif value == 'normal': self.italic = False def setBold(self, value): - if value == "bold": + if value == 'bold': self.bold = True - elif value == "normal": + elif value == 'normal': self.bold = False def setFixed(self, value): self.fixed = value def setUnderlined(self, value): - if value and value != "none": + if value and value != 'none': self.underlined = True def setStrikethrough(self, value): - if value and value != "none": + if value and value != 'none': self.strikethrough = True def setPosition(self, value): @@ -83,10 +83,10 @@ class TextProps: posisize = value.split(' ') textpos = posisize[0] if textpos.find('%') == -1: - if textpos == "sub": + if textpos == 'sub': self.superscript = False self.subscript = True - elif textpos == "super": + elif textpos == 'super': self.superscript = True self.subscript = False else: @@ -100,14 +100,14 @@ class TextProps: def __unicode__(self): - return "[italic={}, bold=i{}, fixed={}]".format(unicode_type(self.italic), + return '[italic={}, bold=i{}, fixed={}]'.format(unicode_type(self.italic), unicode_type(self.bold), unicode_type(self.fixed)) __str__ = __unicode__ class ParagraphProps: - """ Holds properties of a paragraph style. """ + ''' Holds properties of a paragraph style. ''' def __init__(self): @@ -131,14 +131,14 @@ class ParagraphProps: def __unicode__(self): - return "[bq=%s, h=%d, code=%s]" % (unicode_type(self.blockquote), + return '[bq=%s, h=%d, code=%s]' % (unicode_type(self.blockquote), self.headingLevel, unicode_type(self.code)) __str__ = __unicode__ class ListProperties: - """ Holds properties for a list style. """ + ''' Holds properties for a list style. ''' def __init__(self): self.ordered = False @@ -152,8 +152,8 @@ class ODF2MoinMoin: def __init__(self, filepath): self.footnotes = [] self.footnoteCounter = 0 - self.textStyles = {"Standard": TextProps()} - self.paragraphStyles = {"Standard": ParagraphProps()} + self.textStyles = {'Standard': TextProps()} + self.paragraphStyles = {'Standard': ParagraphProps()} self.listStyles = {} self.fixedFonts = [] self.hasTitle = 0 @@ -180,43 +180,43 @@ class ODF2MoinMoin: self.load(filepath) def processFontDeclarations(self, fontDecl): - """ Extracts necessary font information from a font-declaration + ''' Extracts necessary font information from a font-declaration element. - """ - for fontFace in fontDecl.getElementsByTagName("style:font-face"): - if fontFace.getAttribute("style:font-pitch") == "fixed": - self.fixedFonts.append(fontFace.getAttribute("style:name")) + ''' + for fontFace in fontDecl.getElementsByTagName('style:font-face'): + if fontFace.getAttribute('style:font-pitch') == 'fixed': + self.fixedFonts.append(fontFace.getAttribute('style:name')) def extractTextProperties(self, style, parent=None): - """ Extracts text properties from a style element. """ + ''' Extracts text properties from a style element. ''' textProps = TextProps() - textPropEl = style.getElementsByTagName("style:text-properties") + textPropEl = style.getElementsByTagName('style:text-properties') if not textPropEl: return textProps textPropEl = textPropEl[0] - textProps.setItalic(textPropEl.getAttribute("fo:font-style")) - textProps.setBold(textPropEl.getAttribute("fo:font-weight")) - textProps.setUnderlined(textPropEl.getAttribute("style:text-underline-style")) - textProps.setStrikethrough(textPropEl.getAttribute("style:text-line-through-style")) - textProps.setPosition(textPropEl.getAttribute("style:text-position")) + textProps.setItalic(textPropEl.getAttribute('fo:font-style')) + textProps.setBold(textPropEl.getAttribute('fo:font-weight')) + textProps.setUnderlined(textPropEl.getAttribute('style:text-underline-style')) + textProps.setStrikethrough(textPropEl.getAttribute('style:text-line-through-style')) + textProps.setPosition(textPropEl.getAttribute('style:text-position')) - if textPropEl.getAttribute("style:font-name") in self.fixedFonts: + if textPropEl.getAttribute('style:font-name') in self.fixedFonts: textProps.setFixed(True) return textProps def extractParagraphProperties(self, style, parent=None): - """ Extracts paragraph properties from a style element. """ + ''' Extracts paragraph properties from a style element. ''' paraProps = ParagraphProps() - name = style.getAttribute("style:name") + name = style.getAttribute('style:name') - if name.startswith("Heading_20_"): + if name.startswith('Heading_20_'): level = name[11:] try: level = int(level) @@ -224,13 +224,13 @@ class ODF2MoinMoin: except: level = 0 - if name == "Title": + if name == 'Title': paraProps.setTitle(True) - paraPropEl = style.getElementsByTagName("style:paragraph-properties") + paraPropEl = style.getElementsByTagName('style:paragraph-properties') if paraPropEl: paraPropEl = paraPropEl[0] - leftMargin = paraPropEl.getAttribute("fo:margin-left") + leftMargin = paraPropEl.getAttribute('fo:margin-left') if leftMargin: try: leftMargin = float(leftMargin[:-2]) @@ -246,23 +246,23 @@ class ODF2MoinMoin: return paraProps def processStyles(self, styleElements): - """ Runs through "style" elements extracting necessary information. - """ + ''' Runs through "style" elements extracting necessary information. + ''' for style in styleElements: - name = style.getAttribute("style:name") + name = style.getAttribute('style:name') - if name == "Standard": + if name == 'Standard': continue - family = style.getAttribute("style:family") - parent = style.getAttribute("style:parent-style-name") + family = style.getAttribute('style:family') + parent = style.getAttribute('style:parent-style-name') - if family == "text": + if family == 'text': self.textStyles[name] = self.extractTextProperties(style, parent) - elif family == "paragraph": + elif family == 'paragraph': self.paragraphStyles[name] = \ self.extractParagraphProperties(style, parent) self.textStyles[name] = self.extractTextProperties(style, parent) @@ -270,49 +270,49 @@ class ODF2MoinMoin: def processListStyles(self, listStyleElements): for style in listStyleElements: - name = style.getAttribute("style:name") + name = style.getAttribute('style:name') prop = ListProperties() if style.hasChildNodes(): subitems = [el for el in style.childNodes - if el.nodeType == xml.dom.Node.ELEMENT_NODE and el.tagName == "text:list-level-style-number"] + if el.nodeType == xml.dom.Node.ELEMENT_NODE and el.tagName == 'text:list-level-style-number'] if len(subitems) > 0: prop.setOrdered(True) self.listStyles[name] = prop def load(self, filepath): - """ Loads an ODT file. """ + ''' Loads an ODT file. ''' zip = zipfile.ZipFile(filepath) - styles_doc = xml.dom.minidom.parseString(zip.read("styles.xml")) - fontfacedecls = styles_doc.getElementsByTagName("office:font-face-decls") + styles_doc = xml.dom.minidom.parseString(zip.read('styles.xml')) + fontfacedecls = styles_doc.getElementsByTagName('office:font-face-decls') if fontfacedecls: self.processFontDeclarations(fontfacedecls[0]) - self.processStyles(styles_doc.getElementsByTagName("style:style")) - self.processListStyles(styles_doc.getElementsByTagName("text:list-style")) + self.processStyles(styles_doc.getElementsByTagName('style:style')) + self.processListStyles(styles_doc.getElementsByTagName('text:list-style')) - self.content = xml.dom.minidom.parseString(zip.read("content.xml")) - fontfacedecls = self.content.getElementsByTagName("office:font-face-decls") + self.content = xml.dom.minidom.parseString(zip.read('content.xml')) + fontfacedecls = self.content.getElementsByTagName('office:font-face-decls') if fontfacedecls: self.processFontDeclarations(fontfacedecls[0]) - self.processStyles(self.content.getElementsByTagName("style:style")) - self.processListStyles(self.content.getElementsByTagName("text:list-style")) + self.processStyles(self.content.getElementsByTagName('style:style')) + self.processListStyles(self.content.getElementsByTagName('text:list-style')) def compressCodeBlocks(self, text): - """ Removes extra blank lines from code blocks. """ + ''' Removes extra blank lines from code blocks. ''' return text - lines = text.split("\n") + lines = text.split('\n') buffer = [] numLines = len(lines) for i in range(numLines): if (lines[i].strip() or i == numLines-1 or i == 0 or - not (lines[i-1].startswith(" ") and lines[i+1].startswith(" "))): - buffer.append("\n" + lines[i]) + not (lines[i-1].startswith(' ') and lines[i+1].startswith(' '))): + buffer.append('\n' + lines[i]) return ''.join(buffer) @@ -321,44 +321,44 @@ class ODF2MoinMoin: return '' def draw_image(self, node): - """ - """ + ''' + ''' - link = node.getAttribute("xlink:href") + link = node.getAttribute('xlink:href') if link and link[:2] == './': # Indicates a sub-object, which isn't supported - return "%s\n" % link + return '%s\n' % link if link and link[:9] == 'Pictures/': link = link[9:] - return "[[Image(%s)]]\n" % link + return '[[Image(%s)]]\n' % link def text_a(self, node): text = self.textToString(node) - link = node.getAttribute("xlink:href") + link = node.getAttribute('xlink:href') if link.strip() == text.strip(): - return "[%s] " % link.strip() + return '[%s] ' % link.strip() else: - return f"[{link.strip()} {text.strip()}] " + return f'[{link.strip()} {text.strip()}] ' def text_line_break(self, node): - return "[[BR]]" + return '[[BR]]' def text_note(self, node): - cite = (node.getElementsByTagName("text:note-citation")[0] + cite = (node.getElementsByTagName('text:note-citation')[0] .childNodes[0].nodeValue) - body = (node.getElementsByTagName("text:note-body")[0] + body = (node.getElementsByTagName('text:note-body')[0] .childNodes[0]) self.footnotes.append((cite, self.textToString(body))) - return "^%s^" % cite + return '^%s^' % cite def text_s(self, node): try: - num = int(node.getAttribute("text:c")) - return " "*num + num = int(node.getAttribute('text:c')) + return ' '*num except: - return " " + return ' ' def text_tab(self, node): - return " " + return ' ' def inline_markup(self, node): text = self.textToString(node) @@ -366,11 +366,11 @@ class ODF2MoinMoin: if not text.strip(): return '' # don't apply styles to white space - styleName = node.getAttribute("text:style-name") + styleName = node.getAttribute('text:style-name') style = self.textStyles.get(styleName, TextProps()) if style.fixed: - return "`" + text + "`" + return '`' + text + '`' mark = [] if style: @@ -379,16 +379,16 @@ class ODF2MoinMoin: if style.bold: mark.append("'''") if style.underlined: - mark.append("__") + mark.append('__') if style.strikethrough: - mark.append("~~") + mark.append('~~') if style.superscript: - mark.append("^") + mark.append('^') if style.subscript: - mark.append(",,") + mark.append(',,') revmark = mark[:] revmark.reverse() - return "{}{}{}".format(''.join(mark), text, ''.join(revmark)) + return '{}{}{}'.format(''.join(mark), text, ''.join(revmark)) # ----------------------------------- def listToString(self, listElement, indent=0): @@ -396,71 +396,71 @@ class ODF2MoinMoin: self.lastsegment = listElement.tagName buffer = [] - styleName = listElement.getAttribute("text:style-name") + styleName = listElement.getAttribute('text:style-name') props = self.listStyles.get(styleName, ListProperties()) i = 0 for item in listElement.childNodes: - buffer.append(" "*indent) + buffer.append(' '*indent) i += 1 if props.ordered: number = unicode_type(i) - number = " " + number + ". " - buffer.append(" 1. ") + number = ' ' + number + '. ' + buffer.append(' 1. ') else: - buffer.append(" * ") + buffer.append(' * ') subitems = [el for el in item.childNodes - if el.tagName in ["text:p", "text:h", "text:list"]] + if el.tagName in ['text:p', 'text:h', 'text:list']] for subitem in subitems: - if subitem.tagName == "text:list": - buffer.append("\n") + if subitem.tagName == 'text:list': + buffer.append('\n') buffer.append(self.listToString(subitem, indent+3)) else: buffer.append(self.paragraphToString(subitem, indent+3)) self.lastsegment = subitem.tagName self.lastsegment = item.tagName - buffer.append("\n") + buffer.append('\n') return ''.join(buffer) def tableToString(self, tableElement): - """ MoinMoin uses || to delimit table cells - """ + ''' MoinMoin uses || to delimit table cells + ''' self.lastsegment = tableElement.tagName buffer = [] for item in tableElement.childNodes: self.lastsegment = item.tagName - if item.tagName == "table:table-header-rows": + if item.tagName == 'table:table-header-rows': buffer.append(self.tableToString(item)) - if item.tagName == "table:table-row": - buffer.append("\n||") + if item.tagName == 'table:table-row': + buffer.append('\n||') for cell in item.childNodes: buffer.append(self.inline_markup(cell)) - buffer.append("||") + buffer.append('||') self.lastsegment = cell.tagName return ''.join(buffer) def toString(self): - """ Converts the document to a string. + ''' Converts the document to a string. FIXME: Result from second call differs from first call - """ - body = self.content.getElementsByTagName("office:body")[0] + ''' + body = self.content.getElementsByTagName('office:body')[0] text = body.childNodes[0] buffer = [] paragraphs = [el for el in text.childNodes - if el.tagName in ["draw:page", "text:p", "text:h","text:section", - "text:list", "table:table"]] + if el.tagName in ['draw:page', 'text:p', 'text:h','text:section', + 'text:list', 'table:table']] for paragraph in paragraphs: - if paragraph.tagName == "text:list": + if paragraph.tagName == 'text:list': text = self.listToString(paragraph) - elif paragraph.tagName == "text:section": + elif paragraph.tagName == 'text:section': text = self.textToString(paragraph) - elif paragraph.tagName == "table:table": + elif paragraph.tagName == 'table:table': text = self.tableToString(paragraph) else: text = self.paragraphToString(paragraph) @@ -469,11 +469,11 @@ class ODF2MoinMoin: if self.footnotes: - buffer.append("----") + buffer.append('----') for cite, body in self.footnotes: - buffer.append(f"{cite}: {body}") + buffer.append(f'{cite}: {body}') - buffer.append("") + buffer.append('') return self.compressCodeBlocks('\n'.join(buffer)) def textToString(self, element): @@ -488,21 +488,21 @@ class ODF2MoinMoin: elif node.nodeType == xml.dom.Node.ELEMENT_NODE: tag = node.tagName - if tag in ("draw:text-box", "draw:frame"): + if tag in ('draw:text-box', 'draw:frame'): buffer.append(self.textToString(node)) - elif tag in ("text:p", "text:h"): + elif tag in ('text:p', 'text:h'): text = self.paragraphToString(node) if text: buffer.append(text) - elif tag == "text:list": + elif tag == 'text:list': buffer.append(self.listToString(node)) else: method = self.elements.get(tag) if method: buffer.append(method(node)) else: - buffer.append(" {" + tag + "} ") + buffer.append(' {' + tag + '} ') return ''.join(buffer) @@ -510,23 +510,23 @@ class ODF2MoinMoin: dummyParaProps = ParagraphProps() - style_name = paragraph.getAttribute("text:style-name") + style_name = paragraph.getAttribute('text:style-name') paraProps = self.paragraphStyles.get(style_name, dummyParaProps) text = self.inline_markup(paragraph) if paraProps and not paraProps.code: text = text.strip() - if paragraph.tagName == "text:p" and self.lastsegment == "text:p": - text = "\n" + text + if paragraph.tagName == 'text:p' and self.lastsegment == 'text:p': + text = '\n' + text self.lastsegment = paragraph.tagName if paraProps.title: self.hasTitle = 1 - return "= " + text + " =\n" + return '= ' + text + ' =\n' - outlinelevel = paragraph.getAttribute("text:outline-level") + outlinelevel = paragraph.getAttribute('text:outline-level') if outlinelevel: level = int(outlinelevel) @@ -534,10 +534,10 @@ class ODF2MoinMoin: level += 1 if level >= 1: - return "=" * level + " " + text + " " + "=" * level + "\n" + return '=' * level + ' ' + text + ' ' + '=' * level + '\n' elif paraProps.code: - return "{{{\n" + text + "\n}}}\n" + return '{{{\n' + text + '\n}}}\n' if paraProps.indented: return self.wrapParagraph(text, indent=indent, blockquote=True) @@ -552,19 +552,19 @@ class ODF2MoinMoin: LIMIT = 50 if blockquote: - buffer.append(" ") + buffer.append(' ') return ''.join(buffer) + text # Unused from here for token in text.split(): if counter > LIMIT - indent: - buffer.append("\n" + " "*indent) + buffer.append('\n' + ' '*indent) if blockquote: - buffer.append(" ") + buffer.append(' ') counter = 0 - buffer.append(token + " ") + buffer.append(token + ' ') counter += len(token) return ''.join(buffer) diff --git a/src/odf/odf2xhtml.py b/src/odf/odf2xhtml.py index d889e02f88..d628ac1eba 100644 --- a/src/odf/odf2xhtml.py +++ b/src/odf/odf2xhtml.py @@ -78,10 +78,10 @@ if False: # Added by Kovid class StyleToCSS: - """ The purpose of the StyleToCSS class is to contain the rules to convert + ''' The purpose of the StyleToCSS class is to contain the rules to convert ODF styles to CSS2. Since it needs the generic fonts, it would probably make sense to also contain the Styles in a dict as well.. - """ + ''' def __init__(self): # Font declarations @@ -92,39 +92,39 @@ class StyleToCSS: self.ruleconversions = { (DRAWNS,'fill-image-name'): self.c_drawfillimage, - (FONS,"background-color"): self.c_fo, - (FONS,"border"): self.c_fo, - (FONS,"border-bottom"): self.c_fo, - (FONS,"border-left"): self.c_fo, - (FONS,"border-right"): self.c_fo, - (FONS,"border-top"): self.c_fo, - (FONS,"break-after"): self.c_break, # Added by Kovid - (FONS,"break-before"): self.c_break, # Added by Kovid - (FONS,"color"): self.c_fo, - (FONS,"font-family"): self.c_fo, - (FONS,"font-size"): self.c_fo, - (FONS,"font-style"): self.c_fo, - (FONS,"font-variant"): self.c_fo, - (FONS,"font-weight"): self.c_fo, - (FONS,"line-height"): self.c_fo, - (FONS,"margin"): self.c_fo, - (FONS,"margin-bottom"): self.c_fo, - (FONS,"margin-left"): self.c_fo, - (FONS,"margin-right"): self.c_fo, - (FONS,"margin-top"): self.c_fo, - (FONS,"min-height"): self.c_fo, - (FONS,"padding"): self.c_fo, - (FONS,"padding-bottom"): self.c_fo, - (FONS,"padding-left"): self.c_fo, - (FONS,"padding-right"): self.c_fo, - (FONS,"padding-top"): self.c_fo, - (FONS,"page-width"): self.c_page_width, - (FONS,"page-height"): self.c_page_height, - (FONS,"text-align"): self.c_text_align, - (FONS,"text-indent") :self.c_fo, + (FONS,'background-color'): self.c_fo, + (FONS,'border'): self.c_fo, + (FONS,'border-bottom'): self.c_fo, + (FONS,'border-left'): self.c_fo, + (FONS,'border-right'): self.c_fo, + (FONS,'border-top'): self.c_fo, + (FONS,'break-after'): self.c_break, # Added by Kovid + (FONS,'break-before'): self.c_break, # Added by Kovid + (FONS,'color'): self.c_fo, + (FONS,'font-family'): self.c_fo, + (FONS,'font-size'): self.c_fo, + (FONS,'font-style'): self.c_fo, + (FONS,'font-variant'): self.c_fo, + (FONS,'font-weight'): self.c_fo, + (FONS,'line-height'): self.c_fo, + (FONS,'margin'): self.c_fo, + (FONS,'margin-bottom'): self.c_fo, + (FONS,'margin-left'): self.c_fo, + (FONS,'margin-right'): self.c_fo, + (FONS,'margin-top'): self.c_fo, + (FONS,'min-height'): self.c_fo, + (FONS,'padding'): self.c_fo, + (FONS,'padding-bottom'): self.c_fo, + (FONS,'padding-left'): self.c_fo, + (FONS,'padding-right'): self.c_fo, + (FONS,'padding-top'): self.c_fo, + (FONS,'page-width'): self.c_page_width, + (FONS,'page-height'): self.c_page_height, + (FONS,'text-align'): self.c_text_align, + (FONS,'text-indent') :self.c_fo, (TABLENS,'border-model') :self.c_border_model, (STYLENS,'column-width') : self.c_width, - (STYLENS,"font-name"): self.c_fn, + (STYLENS,'font-name'): self.c_fn, (STYLENS,'horizontal-pos'): self.c_hp, (STYLENS,'text-position'): self.c_text_position, (STYLENS,'text-line-through-style'): self.c_text_line_through_style, @@ -141,19 +141,19 @@ class StyleToCSS: ODF: roman, swiss, modern, decorative, script, system This method put the font and fallback into a dictionary """ - htmlgeneric = "sans-serif" - if generic == "roman": - htmlgeneric = "serif" - elif generic == "swiss": - htmlgeneric = "sans-serif" - elif generic == "modern": - htmlgeneric = "monospace" - elif generic == "decorative": - htmlgeneric = "sans-serif" - elif generic == "script": - htmlgeneric = "monospace" - elif generic == "system": - htmlgeneric = "serif" + htmlgeneric = 'sans-serif' + if generic == 'roman': + htmlgeneric = 'serif' + elif generic == 'swiss': + htmlgeneric = 'sans-serif' + elif generic == 'modern': + htmlgeneric = 'monospace' + elif generic == 'decorative': + htmlgeneric = 'sans-serif' + elif generic == 'script': + htmlgeneric = 'monospace' + elif generic == 'system': + htmlgeneric = 'serif' self.fontdict[name] = (family, htmlgeneric) def c_drawfillimage(self, ruleset, sdict, rule, val): @@ -164,7 +164,7 @@ class StyleToCSS: sdict['background-image'] = "url('%s')" % self.fillimages[val] def c_fo(self, ruleset, sdict, rule, val): - """ XSL formatting attributes """ + ''' XSL formatting attributes ''' selector = rule[1] sdict[selector] = val @@ -176,29 +176,29 @@ class StyleToCSS: sdict[property] = values.get(val, 'auto') def c_border_model(self, ruleset, sdict, rule, val): - """ Convert to CSS2 border model """ + ''' Convert to CSS2 border model ''' if val == 'collapsing': sdict['border-collapse'] ='collapse' else: sdict['border-collapse'] ='separate' def c_width(self, ruleset, sdict, rule, val): - """ Set width of box """ + ''' Set width of box ''' sdict['width'] = val def c_text_align(self, ruleset, sdict, rule, align): - """ Text align """ - if align == "start": - align = "left" - if align == "end": - align = "right" + ''' Text align ''' + if align == 'start': + align = 'left' + if align == 'end': + align = 'right' sdict['text-align'] = align def c_fn(self, ruleset, sdict, rule, fontstyle): - """ Generate the CSS font family + ''' Generate the CSS font family A generic font can be found in two ways. In a <style:font-face> element or as a font-family-generic attribute in text-properties. - """ + ''' generic = ruleset.get((STYLENS,'font-family-generic')) if generic is not None: self.save_font(fontstyle, fontstyle, generic) @@ -206,7 +206,7 @@ class StyleToCSS: sdict['font-family'] = '%s, %s' % (family, htmlgeneric) def c_text_position(self, ruleset, sdict, rule, tp): - """ Text position. This is used e.g. to make superscript and subscript + ''' Text position. This is used e.g. to make superscript and subscript This attribute can have one or two values. The first value must be present and specifies the vertical @@ -223,15 +223,15 @@ class StyleToCSS: used. Although this value may change the font height that is displayed, it never changes the current font height that is used for additional calculations. - """ + ''' textpos = tp.split(' ') - if len(textpos) == 2 and textpos[0] != "0%": + if len(textpos) == 2 and textpos[0] != '0%': # Bug in OpenOffice. If vertical-align is 0% - ignore the text size. sdict['font-size'] = textpos[1] - if textpos[0] == "super": - sdict['vertical-align'] = "33%" - elif textpos[0] == "sub": - sdict['vertical-align'] = "-33%" + if textpos[0] == 'super': + sdict['vertical-align'] = '33%' + elif textpos[0] == 'sub': + sdict['vertical-align'] = '-33%' else: sdict['vertical-align'] = textpos[0] @@ -241,39 +241,39 @@ class StyleToCSS: # collect the information. wrap = ruleset.get((STYLENS,'wrap'),'parallel') # Can have: from-left, left, center, right, from-inside, inside, outside - if hpos == "center": - sdict['margin-left'] = "auto" - sdict['margin-right'] = "auto" + if hpos == 'center': + sdict['margin-left'] = 'auto' + sdict['margin-right'] = 'auto' # else: # # force it to be *something* then delete it # sdict['margin-left'] = sdict['margin-right'] = '' # del sdict['margin-left'], sdict['margin-right'] - if hpos in ("right","outside"): - if wrap in ("left", "parallel","dynamic"): - sdict['float'] = "right" - elif wrap == "run-through": - sdict['position'] = "absolute" # Simulate run-through - sdict['top'] = "0" - sdict['right'] = "0" + if hpos in ('right','outside'): + if wrap in ('left', 'parallel','dynamic'): + sdict['float'] = 'right' + elif wrap == 'run-through': + sdict['position'] = 'absolute' # Simulate run-through + sdict['top'] = '0' + sdict['right'] = '0' else: # No wrapping - sdict['margin-left'] = "auto" - sdict['margin-right'] = "0px" - elif hpos in ("left", "inside"): - if wrap in ("right", "parallel","dynamic"): - sdict['float'] = "left" - elif wrap == "run-through": - sdict['position'] = "absolute" # Simulate run-through - sdict['top'] = "0" - sdict['left'] = "0" + sdict['margin-left'] = 'auto' + sdict['margin-right'] = '0px' + elif hpos in ('left', 'inside'): + if wrap in ('right', 'parallel','dynamic'): + sdict['float'] = 'left' + elif wrap == 'run-through': + sdict['position'] = 'absolute' # Simulate run-through + sdict['top'] = '0' + sdict['left'] = '0' else: # No wrapping - sdict['margin-left'] = "0px" - sdict['margin-right'] = "auto" - elif hpos in ("from-left", "from-inside"): - if wrap in ("right", "parallel"): - sdict['float'] = "left" + sdict['margin-left'] = '0px' + sdict['margin-right'] = 'auto' + elif hpos in ('from-left', 'from-inside'): + if wrap in ('right', 'parallel'): + sdict['float'] = 'left' else: - sdict['position'] = "relative" # No wrapping + sdict['position'] = 'relative' # No wrapping if (SVGNS,'x') in ruleset: sdict['left'] = ruleset[(SVGNS,'x')] @@ -287,18 +287,18 @@ class StyleToCSS: """ Set underline decoration HTML doesn't really have a page-width. It is always 100% of the browser width """ - if val and val != "none": - sdict['text-decoration'] = "underline" + if val and val != 'none': + sdict['text-decoration'] = 'underline' def c_text_line_through_style(self, ruleset, sdict, rule, val): """ Set underline decoration HTML doesn't really have a page-width. It is always 100% of the browser width """ - if val and val != "none": - sdict['text-decoration'] = "line-through" + if val and val != 'none': + sdict['text-decoration'] = 'line-through' def c_page_height(self, ruleset, sdict, rule, val): - """ Set height of box """ + ''' Set height of box ''' sdict['height'] = val def convert_styles(self, ruleset): @@ -333,7 +333,7 @@ class TagStack: return item[1] def rfindattr(self, attr): - """ Find a tag with the given attribute """ + ''' Find a tag with the given attribute ''' for tag, attrs in self.stack: if attr in attrs: return attrs[attr] @@ -379,7 +379,7 @@ special_styles = { class ODF2XHTML(handler.ContentHandler): - """ The ODF2XHTML parses an ODF file and produces XHTML""" + ''' The ODF2XHTML parses an ODF file and produces XHTML''' def __init__(self, generate_css=True, embedable=False): # Tags @@ -398,7 +398,7 @@ class ODF2XHTML(handler.ContentHandler): (DRAWNS, 'frame'): (self.s_draw_frame, self.e_draw_frame), (DRAWNS, 'image'): (self.s_draw_image, None), (DRAWNS, 'fill-image'): (self.s_draw_fill_image, None), - (DRAWNS, "layer-set"):(self.s_ignorexml, None), + (DRAWNS, 'layer-set'):(self.s_ignorexml, None), (DRAWNS, 'object'): (self.s_draw_object, None), (DRAWNS, 'object-ole'): (self.s_draw_object_ole, None), (DRAWNS, 'page'): (self.s_draw_page, self.e_draw_page), @@ -407,47 +407,47 @@ class ODF2XHTML(handler.ContentHandler): (METANS, 'generator'):(self.s_processcont, self.e_dc_metatag), (METANS, 'initial-creator'): (self.s_processcont, self.e_dc_metatag), (METANS, 'keyword'): (self.s_processcont, self.e_dc_metatag), - (NUMBERNS, "boolean-style"):(self.s_ignorexml, None), - (NUMBERNS, "currency-style"):(self.s_ignorexml, None), - (NUMBERNS, "date-style"):(self.s_ignorexml, None), - (NUMBERNS, "number-style"):(self.s_ignorexml, None), - (NUMBERNS, "text-style"):(self.s_ignorexml, None), - (OFFICENS, "annotation"):(self.s_ignorexml, None), - (OFFICENS, "automatic-styles"):(self.s_office_automatic_styles, None), - (OFFICENS, "document"):(self.s_office_document_content, self.e_office_document_content), - (OFFICENS, "document-content"):(self.s_office_document_content, self.e_office_document_content), - (OFFICENS, "forms"):(self.s_ignorexml, None), - (OFFICENS, "master-styles"):(self.s_office_master_styles, None), - (OFFICENS, "meta"):(self.s_ignorecont, None), - (OFFICENS, "presentation"):(self.s_office_presentation, self.e_office_presentation), - (OFFICENS, "spreadsheet"):(self.s_office_spreadsheet, self.e_office_spreadsheet), - (OFFICENS, "styles"):(self.s_office_styles, None), - (OFFICENS, "text"):(self.s_office_text, self.e_office_text), - (OFFICENS, "scripts"):(self.s_ignorexml, None), - (OFFICENS, "settings"):(self.s_ignorexml, None), - (PRESENTATIONNS, "notes"):(self.s_ignorexml, None), + (NUMBERNS, 'boolean-style'):(self.s_ignorexml, None), + (NUMBERNS, 'currency-style'):(self.s_ignorexml, None), + (NUMBERNS, 'date-style'):(self.s_ignorexml, None), + (NUMBERNS, 'number-style'):(self.s_ignorexml, None), + (NUMBERNS, 'text-style'):(self.s_ignorexml, None), + (OFFICENS, 'annotation'):(self.s_ignorexml, None), + (OFFICENS, 'automatic-styles'):(self.s_office_automatic_styles, None), + (OFFICENS, 'document'):(self.s_office_document_content, self.e_office_document_content), + (OFFICENS, 'document-content'):(self.s_office_document_content, self.e_office_document_content), + (OFFICENS, 'forms'):(self.s_ignorexml, None), + (OFFICENS, 'master-styles'):(self.s_office_master_styles, None), + (OFFICENS, 'meta'):(self.s_ignorecont, None), + (OFFICENS, 'presentation'):(self.s_office_presentation, self.e_office_presentation), + (OFFICENS, 'spreadsheet'):(self.s_office_spreadsheet, self.e_office_spreadsheet), + (OFFICENS, 'styles'):(self.s_office_styles, None), + (OFFICENS, 'text'):(self.s_office_text, self.e_office_text), + (OFFICENS, 'scripts'):(self.s_ignorexml, None), + (OFFICENS, 'settings'):(self.s_ignorexml, None), + (PRESENTATIONNS, 'notes'):(self.s_ignorexml, None), # (STYLENS, "default-page-layout"):(self.s_style_default_page_layout, self.e_style_page_layout), - (STYLENS, "default-page-layout"):(self.s_ignorexml, None), - (STYLENS, "default-style"):(self.s_style_default_style, self.e_style_default_style), - (STYLENS, "drawing-page-properties"):(self.s_style_handle_properties, None), - (STYLENS, "font-face"):(self.s_style_font_face, None), + (STYLENS, 'default-page-layout'):(self.s_ignorexml, None), + (STYLENS, 'default-style'):(self.s_style_default_style, self.e_style_default_style), + (STYLENS, 'drawing-page-properties'):(self.s_style_handle_properties, None), + (STYLENS, 'font-face'):(self.s_style_font_face, None), # (STYLENS, "footer"):(self.s_style_footer, self.e_style_footer), # (STYLENS, "footer-style"):(self.s_style_footer_style, None), - (STYLENS, "graphic-properties"):(self.s_style_handle_properties, None), - (STYLENS, "handout-master"):(self.s_ignorexml, None), + (STYLENS, 'graphic-properties'):(self.s_style_handle_properties, None), + (STYLENS, 'handout-master'):(self.s_ignorexml, None), # (STYLENS, "header"):(self.s_style_header, self.e_style_header), # (STYLENS, "header-footer-properties"):(self.s_style_handle_properties, None), # (STYLENS, "header-style"):(self.s_style_header_style, None), - (STYLENS, "master-page"):(self.s_style_master_page, None), - (STYLENS, "page-layout-properties"):(self.s_style_handle_properties, None), - (STYLENS, "page-layout"):(self.s_style_page_layout, self.e_style_page_layout), + (STYLENS, 'master-page'):(self.s_style_master_page, None), + (STYLENS, 'page-layout-properties'):(self.s_style_handle_properties, None), + (STYLENS, 'page-layout'):(self.s_style_page_layout, self.e_style_page_layout), # (STYLENS, "page-layout"):(self.s_ignorexml, None), - (STYLENS, "paragraph-properties"):(self.s_style_handle_properties, None), - (STYLENS, "style"):(self.s_style_style, self.e_style_style), - (STYLENS, "table-cell-properties"):(self.s_style_handle_properties, None), - (STYLENS, "table-column-properties"):(self.s_style_handle_properties, None), - (STYLENS, "table-properties"):(self.s_style_handle_properties, None), - (STYLENS, "text-properties"):(self.s_style_handle_properties, None), + (STYLENS, 'paragraph-properties'):(self.s_style_handle_properties, None), + (STYLENS, 'style'):(self.s_style_style, self.e_style_style), + (STYLENS, 'table-cell-properties'):(self.s_style_handle_properties, None), + (STYLENS, 'table-column-properties'):(self.s_style_handle_properties, None), + (STYLENS, 'table-properties'):(self.s_style_handle_properties, None), + (STYLENS, 'text-properties'):(self.s_style_handle_properties, None), (SVGNS, 'desc'): (self.s_ignorexml, None), (TABLENS, 'covered-table-cell'): (self.s_ignorexml, None), (TABLENS, 'table-cell'): (self.s_table_table_cell, self.e_table_table_cell), @@ -455,9 +455,9 @@ class ODF2XHTML(handler.ContentHandler): (TABLENS, 'table-row'): (self.s_table_table_row, self.e_table_table_row), (TABLENS, 'table'): (self.s_table_table, self.e_table_table), (TEXTNS, 'a'): (self.s_text_a, self.e_text_a), - (TEXTNS, "alphabetical-index-source"):(self.s_text_x_source, self.e_text_x_source), - (TEXTNS, "bibliography-configuration"):(self.s_ignorexml, None), - (TEXTNS, "bibliography-source"):(self.s_text_x_source, self.e_text_x_source), + (TEXTNS, 'alphabetical-index-source'):(self.s_text_x_source, self.e_text_x_source), + (TEXTNS, 'bibliography-configuration'):(self.s_ignorexml, None), + (TEXTNS, 'bibliography-source'):(self.s_text_x_source, self.e_text_x_source), (TEXTNS, 'bookmark'): (self.s_text_bookmark, None), (TEXTNS, 'bookmark-start'): (self.s_text_bookmark, None), (TEXTNS, 'reference-mark-start'): (self.s_text_bookmark, None), # Added by Kovid @@ -465,46 +465,46 @@ class ODF2XHTML(handler.ContentHandler): (TEXTNS, 'reference-ref'): (self.s_text_bookmark_ref, self.e_text_a), # Added by Kovid (TEXTNS, 'bookmark-ref-start'): (self.s_text_bookmark_ref, None), (TEXTNS, 'h'): (self.s_text_h, self.e_text_h), - (TEXTNS, "illustration-index-source"):(self.s_text_x_source, self.e_text_x_source), + (TEXTNS, 'illustration-index-source'):(self.s_text_x_source, self.e_text_x_source), (TEXTNS, 'line-break'):(self.s_text_line_break, None), - (TEXTNS, "linenumbering-configuration"):(self.s_ignorexml, None), - (TEXTNS, "list"):(self.s_text_list, self.e_text_list), - (TEXTNS, "list-item"):(self.s_text_list_item, self.e_text_list_item), - (TEXTNS, "list-level-style-bullet"):(self.s_text_list_level_style_bullet, self.e_text_list_level_style_bullet), - (TEXTNS, "list-level-style-number"):(self.s_text_list_level_style_number, self.e_text_list_level_style_number), - (TEXTNS, "list-style"):(None, None), - (TEXTNS, "note"):(self.s_text_note, None), - (TEXTNS, "note-body"):(self.s_text_note_body, self.e_text_note_body), - (TEXTNS, "note-citation"):(None, self.e_text_note_citation), - (TEXTNS, "notes-configuration"):(self.s_ignorexml, None), - (TEXTNS, "object-index-source"):(self.s_text_x_source, self.e_text_x_source), + (TEXTNS, 'linenumbering-configuration'):(self.s_ignorexml, None), + (TEXTNS, 'list'):(self.s_text_list, self.e_text_list), + (TEXTNS, 'list-item'):(self.s_text_list_item, self.e_text_list_item), + (TEXTNS, 'list-level-style-bullet'):(self.s_text_list_level_style_bullet, self.e_text_list_level_style_bullet), + (TEXTNS, 'list-level-style-number'):(self.s_text_list_level_style_number, self.e_text_list_level_style_number), + (TEXTNS, 'list-style'):(None, None), + (TEXTNS, 'note'):(self.s_text_note, None), + (TEXTNS, 'note-body'):(self.s_text_note_body, self.e_text_note_body), + (TEXTNS, 'note-citation'):(None, self.e_text_note_citation), + (TEXTNS, 'notes-configuration'):(self.s_ignorexml, None), + (TEXTNS, 'object-index-source'):(self.s_text_x_source, self.e_text_x_source), (TEXTNS, 'p'): (self.s_text_p, self.e_text_p), (TEXTNS, 's'): (self.s_text_s, None), (TEXTNS, 'span'): (self.s_text_span, self.e_text_span), (TEXTNS, 'tab'): (self.s_text_tab, None), - (TEXTNS, "table-index-source"):(self.s_text_x_source, self.e_text_x_source), - (TEXTNS, "table-of-content-source"):(self.s_text_x_source, self.e_text_x_source), - (TEXTNS, "user-index-source"):(self.s_text_x_source, self.e_text_x_source), + (TEXTNS, 'table-index-source'):(self.s_text_x_source, self.e_text_x_source), + (TEXTNS, 'table-of-content-source'):(self.s_text_x_source, self.e_text_x_source), + (TEXTNS, 'user-index-source'):(self.s_text_x_source, self.e_text_x_source), } if embedable: self.make_embedable() self._resetobject() def set_plain(self): - """ Tell the parser to not generate CSS """ + ''' Tell the parser to not generate CSS ''' self.generate_css = False def set_embedable(self): - """ Tells the converter to only output the parts inside the <body>""" - self.elements[(OFFICENS, "text")] = (None,None) - self.elements[(OFFICENS, "spreadsheet")] = (None,None) - self.elements[(OFFICENS, "presentation")] = (None,None) - self.elements[(OFFICENS, "document-content")] = (None,None) + ''' Tells the converter to only output the parts inside the <body>''' + self.elements[(OFFICENS, 'text')] = (None,None) + self.elements[(OFFICENS, 'spreadsheet')] = (None,None) + self.elements[(OFFICENS, 'presentation')] = (None,None) + self.elements[(OFFICENS, 'document-content')] = (None,None) def add_style_file(self, stylefilename, media=None): - """ Add a link to an external style file. + ''' Add a link to an external style file. Also turns of the embedding of styles in the HTML - """ + ''' self.use_internal_css = False self.stylefilename = stylefilename if media: @@ -558,30 +558,30 @@ class ODF2XHTML(handler.ContentHandler): self.writeout(escape(d)) def opentag(self, tag, attrs={}, block=False): - """ Create an open HTML tag """ + ''' Create an open HTML tag ''' self.htmlstack.append((tag,attrs,block)) a = [] for key,val in attrs.items(): a.append(f'''{key}={quoteattr(val)}''') if len(a) == 0: - self.writeout("<%s>" % tag) + self.writeout('<%s>' % tag) else: - self.writeout("<{} {}>".format(tag, " ".join(a))) + self.writeout('<{} {}>'.format(tag, ' '.join(a))) if block: - self.writeout("\n") + self.writeout('\n') def closetag(self, tag, block=True): - """ Close an open HTML tag """ + ''' Close an open HTML tag ''' self.htmlstack.pop() - self.writeout("</%s>" % tag) + self.writeout('</%s>' % tag) if block: - self.writeout("\n") + self.writeout('\n') def emptytag(self, tag, attrs={}): a = [] for key,val in attrs.items(): a.append(f'''{key}={quoteattr(val)}''') - self.writeout("<{} {}/>\n".format(tag, " ".join(a))) + self.writeout('<{} {}/>\n'.format(tag, ' '.join(a))) # -------------------------------------------------- # Interface to parser @@ -624,30 +624,30 @@ class ODF2XHTML(handler.ContentHandler): pass def s_ignorexml(self, tag, attrs): - """ Ignore this xml element and all children of it + ''' Ignore this xml element and all children of it It will automatically stop ignoring - """ + ''' self.processelem = False def s_ignorecont(self, tag, attrs): - """ Stop processing the text nodes """ + ''' Stop processing the text nodes ''' self.processcont = False def s_processcont(self, tag, attrs): - """ Start processing the text nodes """ + ''' Start processing the text nodes ''' self.processcont = True def classname(self, attrs): - """ Generate a class name from a style name """ + ''' Generate a class name from a style name ''' c = attrs.get((TEXTNS,'style-name'),'') - c = c.replace(".","_") + c = c.replace('.','_') return c def get_anchor(self, name): - """ Create a unique anchor id for a href name """ + ''' Create a unique anchor id for a href name ''' if name not in self.anchors: # Changed by Kovid - self.anchors[name] = "anchor%d" % (len(self.anchors) + 1) + self.anchors[name] = 'anchor%d' % (len(self.anchors) + 1) return self.anchors.get(name) def purgedata(self): @@ -659,109 +659,109 @@ class ODF2XHTML(handler.ContentHandler): # # ----------------------------------------------------------------------------- def e_dc_title(self, tag, attrs): - """ Get the title from the meta data and create a HTML <title> - """ + ''' Get the title from the meta data and create a HTML <title> + ''' self.title = ''.join(self.data) # self.metatags.append('<title>%s\n' % escape(self.title)) self.data = [] def e_dc_metatag(self, tag, attrs): - """ Any other meta data is added as a element - """ + ''' Any other meta data is added as a element + ''' self.metatags.append('\n'.format(tag[1], quoteattr(''.join(self.data)))) self.data = [] def e_dc_contentlanguage(self, tag, attrs): - """ Set the content language. Identifies the targeted audience - """ + ''' Set the content language. Identifies the targeted audience + ''' self.language = ''.join(self.data) self.metatags.append('\n' % escape(self.language)) self.data = [] def e_dc_creator(self, tag, attrs): - """ Set the content creator. Identifies the targeted audience - """ + ''' Set the content creator. Identifies the targeted audience + ''' self.creator = ''.join(self.data) self.metatags.append('\n' % escape(self.creator)) self.data = [] def s_custom_shape(self, tag, attrs): - """ A is made into a
in HTML which is then styled - """ + ''' A is made into a
in HTML which is then styled + ''' anchor_type = attrs.get((TEXTNS,'anchor-type'),'notfound') htmltag = 'div' - name = "G-" + attrs.get((DRAWNS,'style-name'), "") + name = 'G-' + attrs.get((DRAWNS,'style-name'), '') if name == 'G-': - name = "PR-" + attrs.get((PRESENTATIONNS,'style-name'), "") - name = name.replace(".","_") - if anchor_type == "paragraph": + name = 'PR-' + attrs.get((PRESENTATIONNS,'style-name'), '') + name = name.replace('.','_') + if anchor_type == 'paragraph': style = 'position:absolute;' elif anchor_type == 'char': - style = "position:absolute;" + style = 'position:absolute;' elif anchor_type == 'as-char': htmltag = 'div' style = '' else: - style = "position: absolute;" - if (SVGNS,"width") in attrs: - style = style + "width:" + attrs[(SVGNS,"width")] + ";" - if (SVGNS,"height") in attrs: - style = style + "height:" + attrs[(SVGNS,"height")] + ";" - if (SVGNS,"x") in attrs: - style = style + "left:" + attrs[(SVGNS,"x")] + ";" - if (SVGNS,"y") in attrs: - style = style + "top:" + attrs[(SVGNS,"y")] + ";" + style = 'position: absolute;' + if (SVGNS,'width') in attrs: + style = style + 'width:' + attrs[(SVGNS,'width')] + ';' + if (SVGNS,'height') in attrs: + style = style + 'height:' + attrs[(SVGNS,'height')] + ';' + if (SVGNS,'x') in attrs: + style = style + 'left:' + attrs[(SVGNS,'x')] + ';' + if (SVGNS,'y') in attrs: + style = style + 'top:' + attrs[(SVGNS,'y')] + ';' if self.generate_css: self.opentag(htmltag, {'class': name, 'style': style}) else: self.opentag(htmltag) def e_custom_shape(self, tag, attrs): - """ End the - """ + ''' End the + ''' self.closetag('div') def s_draw_frame(self, tag, attrs): - """ A is made into a
in HTML which is then styled - """ + ''' A is made into a
in HTML which is then styled + ''' self.frame_stack.append([]) anchor_type = attrs.get((TEXTNS,'anchor-type'),'notfound') htmltag = 'div' - name = "G-" + attrs.get((DRAWNS,'style-name'), "") + name = 'G-' + attrs.get((DRAWNS,'style-name'), '') if name == 'G-': - name = "PR-" + attrs.get((PRESENTATIONNS,'style-name'), "") - name = name.replace(".","_") - if anchor_type == "paragraph": + name = 'PR-' + attrs.get((PRESENTATIONNS,'style-name'), '') + name = name.replace('.','_') + if anchor_type == 'paragraph': style = 'position:relative;' elif anchor_type == 'char': - style = "position:relative;" + style = 'position:relative;' elif anchor_type == 'as-char': htmltag = 'div' style = '' else: - style = "position:absolute;" - if (SVGNS,"width") in attrs: - style = style + "width:" + attrs[(SVGNS,"width")] + ";" - if (SVGNS,"height") in attrs: - style = style + "height:" + attrs[(SVGNS,"height")] + ";" - if (SVGNS,"x") in attrs: - style = style + "left:" + attrs[(SVGNS,"x")] + ";" - if (SVGNS,"y") in attrs: - style = style + "top:" + attrs[(SVGNS,"y")] + ";" + style = 'position:absolute;' + if (SVGNS,'width') in attrs: + style = style + 'width:' + attrs[(SVGNS,'width')] + ';' + if (SVGNS,'height') in attrs: + style = style + 'height:' + attrs[(SVGNS,'height')] + ';' + if (SVGNS,'x') in attrs: + style = style + 'left:' + attrs[(SVGNS,'x')] + ';' + if (SVGNS,'y') in attrs: + style = style + 'top:' + attrs[(SVGNS,'y')] + ';' if self.generate_css: self.opentag(htmltag, {'class': name, 'style': style}) else: self.opentag(htmltag) def e_draw_frame(self, tag, attrs): - """ End the - """ + ''' End the + ''' self.closetag('div') self.frame_stack.pop() def s_draw_fill_image(self, tag, attrs): - name = attrs.get((DRAWNS,'name'), "NoName") - imghref = attrs[(XLINKNS,"href")] + name = attrs.get((DRAWNS,'name'), 'NoName') + imghref = attrs[(XLINKNS,'href')] imghref = self.rewritelink(imghref) self.cs.fillimages[name] = imghref @@ -772,27 +772,27 @@ class ODF2XHTML(handler.ContentHandler): return imghref def s_draw_image(self, tag, attrs): - """ A becomes an element - """ + ''' A becomes an element + ''' if self.frame_stack: if self.frame_stack[-1]: return self.frame_stack[-1].append('img') parent = self.tagstack.stackparent() anchor_type = parent.get((TEXTNS,'anchor-type')) - imghref = attrs[(XLINKNS,"href")] + imghref = attrs[(XLINKNS,'href')] imghref = self.rewritelink(imghref) - htmlattrs = {'alt':"", 'src':imghref} + htmlattrs = {'alt':'', 'src':imghref} if self.generate_css: - if anchor_type != "char": - htmlattrs['style'] = "display: block;" + if anchor_type != 'char': + htmlattrs['style'] = 'display: block;' self.emptytag('img', htmlattrs) def s_draw_object(self, tag, attrs): - """ A is embedded object in the document (e.g. spreadsheet in presentation). - """ + ''' A is embedded object in the document (e.g. spreadsheet in presentation). + ''' return # Added by Kovid - objhref = attrs[(XLINKNS,"href")] + objhref = attrs[(XLINKNS,'href')] # Remove leading "./": from "./Object 1" to "Object 1" # objhref = objhref [2:] @@ -804,29 +804,29 @@ class ODF2XHTML(handler.ContentHandler): self._walknode(c.topnode) def s_draw_object_ole(self, tag, attrs): - """ A is embedded OLE object in the document (e.g. MS Graph). - """ + ''' A is embedded OLE object in the document (e.g. MS Graph). + ''' try: - class_id = attrs[(DRAWNS,"class-id")] + class_id = attrs[(DRAWNS,'class-id')] except KeyError: # Added by Kovid to ignore without the right return # attributes - if class_id and class_id.lower() == "00020803-0000-0000-c000-000000000046": # Microsoft Graph 97 Chart + if class_id and class_id.lower() == '00020803-0000-0000-c000-000000000046': # Microsoft Graph 97 Chart tagattrs = {'name':'object_ole_graph', 'class':'ole-graph'} self.opentag('a', tagattrs) self.closetag('a', tagattrs) def s_draw_page(self, tag, attrs): - """ A is a slide in a presentation. We use a
element in HTML. + ''' A is a slide in a presentation. We use a
element in HTML. Therefore if you convert a ODP file, you get a series of
s. Override this for your own purpose. - """ - name = attrs.get((DRAWNS,'name'), "NoName") - stylename = attrs.get((DRAWNS,'style-name'), "") - stylename = stylename.replace(".","_") - masterpage = attrs.get((DRAWNS,'master-page-name'),"") - masterpage = masterpage.replace(".","_") + ''' + name = attrs.get((DRAWNS,'name'), 'NoName') + stylename = attrs.get((DRAWNS,'style-name'), '') + stylename = stylename.replace('.','_') + masterpage = attrs.get((DRAWNS,'master-page-name'),'') + masterpage = masterpage.replace('.','_') if self.generate_css: - self.opentag('fieldset', {'class':f"DP-{stylename} MP-{masterpage}"}) + self.opentag('fieldset', {'class':f'DP-{stylename} MP-{masterpage}'}) else: self.opentag('fieldset') self.opentag('legend') @@ -838,20 +838,20 @@ class ODF2XHTML(handler.ContentHandler): def s_draw_textbox(self, tag, attrs): style = '' - if (FONS,"min-height") in attrs: - style = style + "min-height:" + attrs[(FONS,"min-height")] + ";" + if (FONS,'min-height') in attrs: + style = style + 'min-height:' + attrs[(FONS,'min-height')] + ';' self.opentag('div') # self.opentag('div', {'style': style}) def e_draw_textbox(self, tag, attrs): - """ End the - """ + ''' End the + ''' self.closetag('div') def html_body(self, tag, attrs): self.writedata() if self.generate_css and self.use_internal_css: - self.opentag('style', {'type':"text/css"}, True) + self.opentag('style', {'type':'text/css'}, True) self.writeout('/**/\n') @@ -864,7 +864,7 @@ class ODF2XHTML(handler.ContentHandler): # Specifying an explicit bg color prevents ebook readers # from successfully inverting colors # Added styling for endnotes - default_styles = """ + default_styles = ''' img { width: 100%; height: 100%; } * { padding: 0; margin: 0; } body { margin: 0 1em; } @@ -875,7 +875,7 @@ dl.notes dt { font-size: large } dl.notes dt a { text-decoration: none } dl.notes dd { page-break-after: always } dl.notes dd:last-of-type { page-break-after: avoid } -""" +''' def generate_stylesheet(self): for name in self.stylestack: @@ -923,10 +923,10 @@ dl.notes dd:last-of-type { page-break-after: avoid } yield k, v for css2, names in css_styles.items(): - self.writeout("%s {\n" % ', '.join(names)) + self.writeout('%s {\n' % ', '.join(names)) for style, val in filter_margins(css2): - self.writeout(f"\t{style}: {val};\n") - self.writeout("}\n") + self.writeout(f'\t{style}: {val};\n') + self.writeout('}\n') def generate_footnotes(self): if self.currentnote == 0: @@ -940,13 +940,13 @@ dl.notes dd:last-of-type { page-break-after: avoid } for key in range(1,self.currentnote+1): note = self.notedict[key] # for key,note in self.notedict.items(): - self.opentag('dt', {'id':"footnote-%d" % key}) + self.opentag('dt', {'id':'footnote-%d' % key}) # self.opentag('sup') # self.writeout(escape(note['citation'])) # self.closetag('sup', False) self.writeout('[') - self.opentag('a', {'href': "#citation-%d" % key}) - self.writeout("←%d" % key) + self.opentag('a', {'href': '#citation-%d' % key}) + self.writeout('←%d' % key) self.closetag('a') self.writeout(']\xa0') self.closetag('dt') @@ -957,35 +957,35 @@ dl.notes dd:last-of-type { page-break-after: avoid } def s_office_automatic_styles(self, tag, attrs): if self.xmlfile == 'styles.xml': - self.autoprefix = "A" + self.autoprefix = 'A' else: - self.autoprefix = "" + self.autoprefix = '' def s_office_document_content(self, tag, attrs): - """ First tag in the content.xml file""" + ''' First tag in the content.xml file''' self.writeout('\n') - self.opentag('html', {'xmlns':"http://www.w3.org/1999/xhtml"}, True) + self.opentag('html', {'xmlns':'http://www.w3.org/1999/xhtml'}, True) self.opentag('head', block=True) - self.emptytag('meta', {'http-equiv':"Content-Type", 'content':"text/html;charset=UTF-8"}) + self.emptytag('meta', {'http-equiv':'Content-Type', 'content':'text/html;charset=UTF-8'}) for metaline in self.metatags: self.writeout(metaline) self.writeout('%s\n' % escape(self.title)) def e_office_document_content(self, tag, attrs): - """ Last tag """ + ''' Last tag ''' self.closetag('html') def s_office_master_styles(self, tag, attrs): - """ """ + ''' ''' def s_office_presentation(self, tag, attrs): """ For some odd reason, OpenOffice Impress doesn't define a default-style for the 'paragraph'. We therefore force a standard when we see it is a presentation """ - self.styledict['p'] = {(FONS,'font-size'): "24pt"} - self.styledict['presentation'] = {(FONS,'font-size'): "24pt"} + self.styledict['p'] = {(FONS,'font-size'): '24pt'} + self.styledict['presentation'] = {(FONS,'font-size'): '24pt'} self.html_body(tag, attrs) def e_office_presentation(self, tag, attrs): @@ -1000,10 +1000,10 @@ dl.notes dd:last-of-type { page-break-after: avoid } self.closetag('body') def s_office_styles(self, tag, attrs): - self.autoprefix = "" + self.autoprefix = '' def s_office_text(self, tag, attrs): - """ OpenDocument text """ + ''' OpenDocument text ''' self.styledict['frame'] = {(STYLENS,'wrap'): 'parallel'} self.html_body(tag, attrs) @@ -1012,9 +1012,9 @@ dl.notes dd:last-of-type { page-break-after: avoid } self.closetag('body') def s_style_handle_properties(self, tag, attrs): - """ Copy all attributes to a struct. + ''' Copy all attributes to a struct. We will later convert them to CSS2 - """ + ''' if self.currentstyle is None: # Added by Kovid return @@ -1027,8 +1027,8 @@ dl.notes dd:last-of-type { page-break-after: avoid } 'table-row':'tr','graphic':'graphic'} def s_style_default_style(self, tag, attrs): - """ A default style is like a style on an HTML tag - """ + ''' A default style is like a style on an HTML tag + ''' family = attrs[(STYLENS,'family')] htmlfamily = self.familymap.get(family,'unknown') self.currentstyle = htmlfamily @@ -1045,13 +1045,13 @@ dl.notes dd:last-of-type { page-break-after: avoid } CSS2: serif, sans-serif, cursive, fantasy, monospace ODF: roman, swiss, modern, decorative, script, system """ - name = attrs[(STYLENS,"name")] - family = attrs[(SVGNS,"font-family")] - generic = attrs.get((STYLENS,'font-family-generic'),"") + name = attrs[(STYLENS,'name')] + family = attrs[(SVGNS,'font-family')] + generic = attrs.get((STYLENS,'font-family-generic'),'') self.cs.save_font(name, family, generic) def s_style_footer(self, tag, attrs): - self.opentag('div', {'id':"footer"}) + self.opentag('div', {'id':'footer'}) self.purgedata() def e_style_footer(self, tag, attrs): @@ -1060,12 +1060,12 @@ dl.notes dd:last-of-type { page-break-after: avoid } self.purgedata() def s_style_footer_style(self, tag, attrs): - self.currentstyle = "@print #footer" + self.currentstyle = '@print #footer' self.stylestack.append(self.currentstyle) self.styledict[self.currentstyle] = {} def s_style_header(self, tag, attrs): - self.opentag('div', {'id':"header"}) + self.opentag('div', {'id':'header'}) self.purgedata() def e_style_header(self, tag, attrs): @@ -1074,14 +1074,14 @@ dl.notes dd:last-of-type { page-break-after: avoid } self.purgedata() def s_style_header_style(self, tag, attrs): - self.currentstyle = "@print #header" + self.currentstyle = '@print #header' self.stylestack.append(self.currentstyle) self.styledict[self.currentstyle] = {} def s_style_default_page_layout(self, tag, attrs): - """ Collect the formatting for the default page layout style. - """ - self.currentstyle = "@page" + ''' Collect the formatting for the default page layout style. + ''' + self.currentstyle = '@page' self.stylestack.append(self.currentstyle) self.styledict[self.currentstyle] = {} @@ -1091,29 +1091,29 @@ dl.notes dd:last-of-type { page-break-after: avoid } It is legal in CSS3, but the rest of the application doesn't specify when to use what page layout """ name = attrs[(STYLENS,'name')] - name = name.replace(".","_") - self.currentstyle = ".PL-" + name + name = name.replace('.','_') + self.currentstyle = '.PL-' + name self.stylestack.append(self.currentstyle) self.styledict[self.currentstyle] = {} def e_style_page_layout(self, tag, attrs): - """ End this style - """ + ''' End this style + ''' self.currentstyle = None def s_style_master_page(self, tag, attrs): - """ Collect the formatting for the page layout style. - """ + ''' Collect the formatting for the page layout style. + ''' name = attrs[(STYLENS,'name')] - name = name.replace(".","_") + name = name.replace('.','_') - self.currentstyle = ".MP-" + name + self.currentstyle = '.MP-' + name self.stylestack.append(self.currentstyle) self.styledict[self.currentstyle] = {('','position'):'relative'} # Then load the pagelayout style if we find it pagelayout = attrs.get((STYLENS,'page-layout-name'), None) if pagelayout: - pagelayout = ".PL-" + pagelayout + pagelayout = '.PL-' + pagelayout if pagelayout in self.styledict: styles = self.styledict[pagelayout] for style, val in styles.items(): @@ -1129,20 +1129,20 @@ dl.notes dd:last-of-type { page-break-after: avoid } 'table-row':'TR', 'graphic':'G'} def s_style_style(self, tag, attrs): - """ Collect the formatting for the style. + ''' Collect the formatting for the style. Styles have scope. The same name can be used for both paragraph and character styles Since CSS has no scope we use a prefix. (Not elegant) In ODF a style can have a parent, these parents can be chained. We may not have encountered the parent yet, but if we have, we resolve it. - """ + ''' name = attrs[(STYLENS,'name')] - name = name.replace(".","_") + name = name.replace('.','_') family = attrs[(STYLENS,'family')] htmlfamily = self.familymap.get(family,'unknown') sfamily = self._familyshort.get(family,'X') - name = f"{self.autoprefix}{sfamily}-{name}" + name = f'{self.autoprefix}{sfamily}-{name}' parent = attrs.get((STYLENS,'parent-style-name')) - self.currentstyle = special_styles.get(name,"."+name) + self.currentstyle = special_styles.get(name,'.'+name) self.stylestack.append(self.currentstyle) if self.currentstyle not in self.styledict: self.styledict[self.currentstyle] = {} @@ -1151,9 +1151,9 @@ dl.notes dd:last-of-type { page-break-after: avoid } # Then load the parent style if we find it if parent: - parent = parent.replace(".", "_") - parent = f"{sfamily}-{parent}" - parent = special_styles.get(parent, "."+parent) + parent = parent.replace('.', '_') + parent = f'{sfamily}-{parent}' + parent = special_styles.get(parent, '.'+parent) if parent in self.styledict: styles = self.styledict[parent] for style, val in styles.items(): @@ -1162,30 +1162,30 @@ dl.notes dd:last-of-type { page-break-after: avoid } self.styledict[self.currentstyle]['__parent-style-name'] = parent def e_style_style(self, tag, attrs): - """ End this style - """ + ''' End this style + ''' self.currentstyle = None def s_table_table(self, tag, attrs): - """ Start a table - """ + ''' Start a table + ''' c = attrs.get((TABLENS,'style-name'), None) if c and self.generate_css: - c = c.replace(".","_") - self.opentag('table',{'class': "T-%s" % c}) + c = c.replace('.','_') + self.opentag('table',{'class': 'T-%s' % c}) else: self.opentag('table') self.purgedata() def e_table_table(self, tag, attrs): - """ End a table - """ + ''' End a table + ''' self.writedata() self.closetag('table') self.purgedata() def s_table_table_cell(self, tag, attrs): - """ Start a table cell """ + ''' Start a table cell ''' # FIXME: number-columns-repeated § 8.1.3 # repeated = int(attrs.get( (TABLENS,'number-columns-repeated'), 1)) htmlattrs = {} @@ -1198,60 +1198,60 @@ dl.notes dd:last-of-type { page-break-after: avoid } c = attrs.get((TABLENS,'style-name')) if c: - htmlattrs['class'] = 'TD-%s' % c.replace(".","_") + htmlattrs['class'] = 'TD-%s' % c.replace('.','_') self.opentag('td', htmlattrs) self.purgedata() def e_table_table_cell(self, tag, attrs): - """ End a table cell """ + ''' End a table cell ''' self.writedata() self.closetag('td') self.purgedata() def s_table_table_column(self, tag, attrs): - """ Start a table column """ + ''' Start a table column ''' c = attrs.get((TABLENS,'style-name'), None) repeated = int(attrs.get((TABLENS,'number-columns-repeated'), 1)) htmlattrs = {} if c: - htmlattrs['class'] = "TC-%s" % c.replace(".","_") + htmlattrs['class'] = 'TC-%s' % c.replace('.','_') for x in range(repeated): self.emptytag('col', htmlattrs) self.purgedata() def s_table_table_row(self, tag, attrs): - """ Start a table row """ + ''' Start a table row ''' # FIXME: table:number-rows-repeated c = attrs.get((TABLENS,'style-name'), None) htmlattrs = {} if c: - htmlattrs['class'] = "TR-%s" % c.replace(".","_") + htmlattrs['class'] = 'TR-%s' % c.replace('.','_') self.opentag('tr', htmlattrs) self.purgedata() def e_table_table_row(self, tag, attrs): - """ End a table row """ + ''' End a table row ''' self.writedata() self.closetag('tr') self.purgedata() def s_text_a(self, tag, attrs): - """ Anchors start """ + ''' Anchors start ''' self.writedata() - href = attrs[(XLINKNS,"href")].split("|")[0] - if href[:1] == "#": # Changed by Kovid - href = "#" + self.get_anchor(href[1:]) + href = attrs[(XLINKNS,'href')].split('|')[0] + if href[:1] == '#': # Changed by Kovid + href = '#' + self.get_anchor(href[1:]) self.opentag('a', {'href':href}) self.purgedata() def e_text_a(self, tag, attrs): - """ End an anchor or bookmark reference """ + ''' End an anchor or bookmark reference ''' self.writedata() self.closetag('a', False) self.purgedata() def s_text_bookmark(self, tag, attrs): - """ Bookmark definition """ + ''' Bookmark definition ''' name = attrs[(TEXTNS,'name')] html_id = self.get_anchor(name) self.writedata() @@ -1260,15 +1260,15 @@ dl.notes dd:last-of-type { page-break-after: avoid } self.purgedata() def s_text_bookmark_ref(self, tag, attrs): - """ Bookmark reference """ + ''' Bookmark reference ''' name = attrs[(TEXTNS,'ref-name')] - html_id = "#" + self.get_anchor(name) + html_id = '#' + self.get_anchor(name) self.writedata() self.opentag('a', {'href':html_id}) self.purgedata() def s_text_h(self, tag, attrs): - """ Headings start """ + ''' Headings start ''' level = int(attrs[(TEXTNS,'outline-level')]) if level > 6: level = 6 # Heading levels go only to 6 in XHTML @@ -1278,18 +1278,18 @@ dl.notes dd:last-of-type { page-break-after: avoid } name = self.classname(attrs) for x in range(level + 1,10): self.headinglevels[x] = 0 - special = special_styles.get("P-"+name) + special = special_styles.get('P-'+name) if special or not self.generate_css: self.opentag('h%s' % level) else: - self.opentag('h%s' % level, {'class':"P-%s" % name}) + self.opentag('h%s' % level, {'class':'P-%s' % name}) self.purgedata() def e_text_h(self, tag, attrs): - """ Headings end + ''' Headings end Side-effect: If there is no title in the metadata, then it is taken from the first heading of any level. - """ + ''' self.writedata() level = int(attrs[(TEXTNS,'outline-level')]) if level > 6: @@ -1303,7 +1303,7 @@ dl.notes dd:last-of-type { page-break-after: avoid } self.title = heading # Changed by Kovid tail = ''.join(self.data) - anchor = self.get_anchor(f"{outline}.{tail}") + anchor = self.get_anchor(f'{outline}.{tail}') anchor2 = self.get_anchor(tail) # Added by kovid to fix #7506 self.opentag('a', {'id': anchor}) self.closetag('a', False) @@ -1313,7 +1313,7 @@ dl.notes dd:last-of-type { page-break-after: avoid } self.purgedata() def s_text_line_break(self, tag, attrs): - """ Force a line break (
) """ + ''' Force a line break (
) ''' self.writedata() self.emptytag('br') self.purgedata() @@ -1329,13 +1329,13 @@ dl.notes dd:last-of-type { page-break-after: avoid } list_id = attrs.get(('http://www.w3.org/XML/1998/namespace', 'id')) level = self.tagstack.count_tags(tag) + 1 if name: - name = name.replace(".","_") + name = name.replace('.','_') else: # FIXME: If a list is contained in a table cell or text box, # the list level must return to 1, even though the table or # textbox itself may be nested within another list. name = self.tagstack.rfindattr((TEXTNS,'style-name')) - list_class = "%s_%d" % (name, level) + list_class = '%s_%d' % (name, level) tag_name = self.listtypes.get(list_class,'ul') number_class = tag_name + list_class if list_id: @@ -1359,25 +1359,25 @@ dl.notes dd:last-of-type { page-break-after: avoid } self.purgedata() def e_text_list(self, tag, attrs): - """ End a list """ + ''' End a list ''' self.writedata() if self.list_class_stack: self.list_class_stack.pop() name = attrs.get((TEXTNS,'style-name')) level = self.tagstack.count_tags(tag) + 1 if name: - name = name.replace(".","_") + name = name.replace('.','_') else: # FIXME: If a list is contained in a table cell or text box, # the list level must return to 1, even though the table or # textbox itself may be nested within another list. name = self.tagstack.rfindattr((TEXTNS,'style-name')) - list_class = "%s_%d" % (name, level) + list_class = '%s_%d' % (name, level) self.closetag(self.listtypes.get(list_class,'ul')) self.purgedata() def s_text_list_item(self, tag, attrs): - """ Start list item """ + ''' Start list item ''' number_class = self.list_class_stack[-1] if self.list_class_stack else None if number_class: self.list_number_map[number_class] += 1 @@ -1385,7 +1385,7 @@ dl.notes dd:last-of-type { page-break-after: avoid } self.purgedata() def e_text_list_item(self, tag, attrs): - """ End list item """ + ''' End list item ''' self.writedata() self.closetag('li') self.purgedata() @@ -1398,14 +1398,14 @@ dl.notes dd:last-of-type { page-break-after: avoid } name = self.tagstack.rfindattr((STYLENS,'name')) level = attrs[(TEXTNS,'level')] self.prevstyle = self.currentstyle - list_class = f"{name}_{level}" + list_class = f'{name}_{level}' self.listtypes[list_class] = 'ul' - self.currentstyle = ".{}_{}".format(name.replace(".","_"), level) + self.currentstyle = '.{}_{}'.format(name.replace('.','_'), level) self.stylestack.append(self.currentstyle) self.styledict[self.currentstyle] = {} level = int(level) - listtype = ("square", "disc", "circle")[level % 3] + listtype = ('square', 'disc', 'circle')[level % 3] self.styledict[self.currentstyle][('','list-style-type')] = listtype def e_text_list_level_style_bullet(self, tag, attrs): @@ -1415,28 +1415,28 @@ dl.notes dd:last-of-type { page-break-after: avoid } def s_text_list_level_style_number(self, tag, attrs): name = self.tagstack.stackparent()[(STYLENS,'name')] level = attrs[(TEXTNS,'level')] - num_format = attrs.get((STYLENS,'num-format'),"1") + num_format = attrs.get((STYLENS,'num-format'),'1') start_value = attrs.get((TEXTNS, 'start-value'), '1') - list_class = f"{name}_{level}" + list_class = f'{name}_{level}' self.prevstyle = self.currentstyle - self.currentstyle = ".{}_{}".format(name.replace(".","_"), level) + self.currentstyle = '.{}_{}'.format(name.replace('.','_'), level) if start_value != '1': self.list_starts[self.currentstyle] = start_value self.listtypes[list_class] = 'ol' self.stylestack.append(self.currentstyle) self.styledict[self.currentstyle] = {} - if num_format == "1": - listtype = "decimal" - elif num_format == "I": - listtype = "upper-roman" - elif num_format == "i": - listtype = "lower-roman" - elif num_format == "A": - listtype = "upper-alpha" - elif num_format == "a": - listtype = "lower-alpha" + if num_format == '1': + listtype = 'decimal' + elif num_format == 'I': + listtype = 'upper-roman' + elif num_format == 'i': + listtype = 'lower-roman' + elif num_format == 'A': + listtype = 'upper-alpha' + elif num_format == 'a': + listtype = 'lower-alpha' else: - listtype = "decimal" + listtype = 'decimal' self.styledict[self.currentstyle][('','list-style-type')] = listtype def e_text_list_level_style_number(self, tag, attrs): @@ -1473,7 +1473,7 @@ dl.notes dd:last-of-type { page-break-after: avoid } self.notedict[self.currentnote]['citation'] = mark self.opentag('sup') self.opentag('a', { - 'href': "#footnote-%s" % self.currentnote, + 'href': '#footnote-%s' % self.currentnote, 'class': 'citation', 'id':'citation-%s' % self.currentnote }) @@ -1485,29 +1485,29 @@ dl.notes dd:last-of-type { page-break-after: avoid } self.closetag('sup') def s_text_p(self, tag, attrs): - """ Paragraph - """ + ''' Paragraph + ''' htmlattrs = {} - specialtag = "p" + specialtag = 'p' c = attrs.get((TEXTNS,'style-name'), None) if c: - c = c.replace(".","_") - specialtag = special_styles.get("P-"+c) + c = c.replace('.','_') + specialtag = special_styles.get('P-'+c) if specialtag is None: specialtag = 'p' if self.generate_css: - htmlattrs['class'] = "P-%s" % c + htmlattrs['class'] = 'P-%s' % c self.opentag(specialtag, htmlattrs) self.purgedata() def e_text_p(self, tag, attrs): - """ End Paragraph - """ - specialtag = "p" + ''' End Paragraph + ''' + specialtag = 'p' c = attrs.get((TEXTNS,'style-name'), None) if c: - c = c.replace(".","_") - specialtag = special_styles.get("P-"+c) + c = c.replace('.','_') + specialtag = special_styles.get('P-'+c) if specialtag is None: specialtag = 'p' self.writedata() @@ -1522,9 +1522,9 @@ dl.notes dd:last-of-type { page-break-after: avoid } # element instead of being part of the text flow. # We don't use an entity for the nbsp as the contents of self.data will # be escaped on writeout. - """ Generate a number of spaces. We use the non breaking space for + ''' Generate a number of spaces. We use the non breaking space for the text:s ODF element. - """ + ''' try: c = int(attrs.get((TEXTNS, 'c'), 1)) except: @@ -1533,9 +1533,9 @@ dl.notes dd:last-of-type { page-break-after: avoid } self.data.append('\u00a0'*c) def s_text_span(self, tag, attrs): - """ The element matches the element in HTML. It is + ''' The element matches the element in HTML. It is typically used to properties of the text. - """ + ''' self.writedata() c = attrs.get((TEXTNS,'style-name'), None) htmlattrs = {} @@ -1543,26 +1543,26 @@ dl.notes dd:last-of-type { page-break-after: avoid } # Apparently LibreOffice does this. special = 'span' if c: - c = c.replace(".","_") - special = special_styles.get("S-"+c) + c = c.replace('.','_') + special = special_styles.get('S-'+c) if special is None: special = 'span' if self.generate_css: - htmlattrs['class'] = "S-%s" % c + htmlattrs['class'] = 'S-%s' % c self.opentag(special, htmlattrs) self.purgedata() def e_text_span(self, tag, attrs): - """ End the """ + ''' End the ''' self.writedata() c = attrs.get((TEXTNS,'style-name'), None) # Changed by Kovid to handle inline special styles defined on tags. # Apparently LibreOffice does this. special = 'span' if c: - c = c.replace(".","_") - special = special_styles.get("S-"+c) + c = c.replace('.','_') + special = special_styles.get('S-'+c) if special is None: special = 'span' @@ -1570,22 +1570,22 @@ dl.notes dd:last-of-type { page-break-after: avoid } self.purgedata() def s_text_tab(self, tag, attrs): - """ Move to the next tabstop. We ignore this in HTML - """ + ''' Move to the next tabstop. We ignore this in HTML + ''' self.writedata() self.writeout(' ') self.purgedata() def s_text_x_source(self, tag, attrs): - """ Various indexes and tables of contents. We ignore those. - """ + ''' Various indexes and tables of contents. We ignore those. + ''' self.writedata() self.purgedata() self.s_ignorexml(tag, attrs) def e_text_x_source(self, tag, attrs): - """ Various indexes and tables of contents. We ignore those. - """ + ''' Various indexes and tables of contents. We ignore those. + ''' self.writedata() self.purgedata() @@ -1596,9 +1596,9 @@ dl.notes dd:last-of-type { page-break-after: avoid } # ----------------------------------------------------------------------------- def load(self, odffile): - """ Loads a document into the parser and parses it. + ''' Loads a document into the parser and parses it. The argument can either be a filename or a document in memory. - """ + ''' self.lines = [] self._wfunc = self._wlines if isinstance(odffile, (bytes, str)) or hasattr(odffile, 'read'): # Added by Kovid @@ -1617,8 +1617,8 @@ dl.notes dd:last-of-type { page-break-after: avoid } self.characters(str(node)) def odf2xhtml(self, odffile): - """ Load a file and return the XHTML - """ + ''' Load a file and return the XHTML + ''' self.load(odffile) return self.xhtml() @@ -1627,8 +1627,8 @@ dl.notes dd:last-of-type { page-break-after: avoid } self.lines.append(s) def xhtml(self): - """ Returns the xhtml - """ + ''' Returns the xhtml + ''' return ''.join(self.lines) def _writecss(self, s): @@ -1639,7 +1639,7 @@ dl.notes dd:last-of-type { page-break-after: avoid } pass def css(self): - """ Returns the CSS content """ + ''' Returns the CSS content ''' self._csslines = [] self._wfunc = self._writecss self.generate_stylesheet() @@ -1658,15 +1658,15 @@ dl.notes dd:last-of-type { page-break-after: avoid } outputfp = sys.stdout else: if addsuffix: - outputfile = outputfile + ".html" - outputfp = open(outputfile, "wb") + outputfile = outputfile + '.html' + outputfp = open(outputfile, 'wb') outputfp.write(self.xhtml().encode('us-ascii','xmlcharrefreplace')) outputfp.close() class ODF2XHTMLembedded(ODF2XHTML): - """ The ODF2XHTML parses an ODF file and produces XHTML""" + ''' The ODF2XHTML parses an ODF file and produces XHTML''' def __init__(self, lines, generate_css=True, embedable=False): self._resetobject() @@ -1683,7 +1683,7 @@ class ODF2XHTMLembedded(ODF2XHTML): (DRAWNS, 'frame'): (self.s_draw_frame, self.e_draw_frame), (DRAWNS, 'image'): (self.s_draw_image, None), (DRAWNS, 'fill-image'): (self.s_draw_fill_image, None), - (DRAWNS, "layer-set"):(self.s_ignorexml, None), + (DRAWNS, 'layer-set'):(self.s_ignorexml, None), (DRAWNS, 'page'): (self.s_draw_page, self.e_draw_page), (DRAWNS, 'object'): (self.s_draw_object, None), (DRAWNS, 'object-ole'): (self.s_draw_object_ole, None), @@ -1692,22 +1692,22 @@ class ODF2XHTMLembedded(ODF2XHTML): # (METANS, 'generator'):(self.s_processcont, self.e_dc_metatag), # (METANS, 'initial-creator'): (self.s_processcont, self.e_dc_metatag), # (METANS, 'keyword'): (self.s_processcont, self.e_dc_metatag), - (NUMBERNS, "boolean-style"):(self.s_ignorexml, None), - (NUMBERNS, "currency-style"):(self.s_ignorexml, None), - (NUMBERNS, "date-style"):(self.s_ignorexml, None), - (NUMBERNS, "number-style"):(self.s_ignorexml, None), - (NUMBERNS, "text-style"):(self.s_ignorexml, None), + (NUMBERNS, 'boolean-style'):(self.s_ignorexml, None), + (NUMBERNS, 'currency-style'):(self.s_ignorexml, None), + (NUMBERNS, 'date-style'):(self.s_ignorexml, None), + (NUMBERNS, 'number-style'):(self.s_ignorexml, None), + (NUMBERNS, 'text-style'):(self.s_ignorexml, None), # (OFFICENS, "automatic-styles"):(self.s_office_automatic_styles, None), # (OFFICENS, "document-content"):(self.s_office_document_content, self.e_office_document_content), - (OFFICENS, "forms"):(self.s_ignorexml, None), + (OFFICENS, 'forms'):(self.s_ignorexml, None), # (OFFICENS, "master-styles"):(self.s_office_master_styles, None), - (OFFICENS, "meta"):(self.s_ignorecont, None), + (OFFICENS, 'meta'):(self.s_ignorecont, None), # (OFFICENS, "presentation"):(self.s_office_presentation, self.e_office_presentation), # (OFFICENS, "spreadsheet"):(self.s_office_spreadsheet, self.e_office_spreadsheet), # (OFFICENS, "styles"):(self.s_office_styles, None), # (OFFICENS, "text"):(self.s_office_text, self.e_office_text), - (OFFICENS, "scripts"):(self.s_ignorexml, None), - (PRESENTATIONNS, "notes"):(self.s_ignorexml, None), + (OFFICENS, 'scripts'):(self.s_ignorexml, None), + (PRESENTATIONNS, 'notes'):(self.s_ignorexml, None), # (STYLENS, "default-page-layout"):(self.s_style_default_page_layout, self.e_style_page_layout), # (STYLENS, "default-page-layout"):(self.s_ignorexml, None), # (STYLENS, "default-style"):(self.s_style_default_style, self.e_style_default_style), @@ -1737,29 +1737,29 @@ class ODF2XHTMLembedded(ODF2XHTML): (TABLENS, 'table-row'): (self.s_table_table_row, self.e_table_table_row), (TABLENS, 'table'): (self.s_table_table, self.e_table_table), (TEXTNS, 'a'): (self.s_text_a, self.e_text_a), - (TEXTNS, "alphabetical-index-source"):(self.s_text_x_source, self.e_text_x_source), - (TEXTNS, "bibliography-configuration"):(self.s_ignorexml, None), - (TEXTNS, "bibliography-source"):(self.s_text_x_source, self.e_text_x_source), + (TEXTNS, 'alphabetical-index-source'):(self.s_text_x_source, self.e_text_x_source), + (TEXTNS, 'bibliography-configuration'):(self.s_ignorexml, None), + (TEXTNS, 'bibliography-source'):(self.s_text_x_source, self.e_text_x_source), (TEXTNS, 'h'): (self.s_text_h, self.e_text_h), - (TEXTNS, "illustration-index-source"):(self.s_text_x_source, self.e_text_x_source), + (TEXTNS, 'illustration-index-source'):(self.s_text_x_source, self.e_text_x_source), (TEXTNS, 'line-break'):(self.s_text_line_break, None), - (TEXTNS, "linenumbering-configuration"):(self.s_ignorexml, None), - (TEXTNS, "list"):(self.s_text_list, self.e_text_list), - (TEXTNS, "list-item"):(self.s_text_list_item, self.e_text_list_item), - (TEXTNS, "list-level-style-bullet"):(self.s_text_list_level_style_bullet, self.e_text_list_level_style_bullet), - (TEXTNS, "list-level-style-number"):(self.s_text_list_level_style_number, self.e_text_list_level_style_number), - (TEXTNS, "list-style"):(None, None), - (TEXTNS, "note"):(self.s_text_note, None), - (TEXTNS, "note-body"):(self.s_text_note_body, self.e_text_note_body), - (TEXTNS, "note-citation"):(None, self.e_text_note_citation), - (TEXTNS, "notes-configuration"):(self.s_ignorexml, None), - (TEXTNS, "object-index-source"):(self.s_text_x_source, self.e_text_x_source), + (TEXTNS, 'linenumbering-configuration'):(self.s_ignorexml, None), + (TEXTNS, 'list'):(self.s_text_list, self.e_text_list), + (TEXTNS, 'list-item'):(self.s_text_list_item, self.e_text_list_item), + (TEXTNS, 'list-level-style-bullet'):(self.s_text_list_level_style_bullet, self.e_text_list_level_style_bullet), + (TEXTNS, 'list-level-style-number'):(self.s_text_list_level_style_number, self.e_text_list_level_style_number), + (TEXTNS, 'list-style'):(None, None), + (TEXTNS, 'note'):(self.s_text_note, None), + (TEXTNS, 'note-body'):(self.s_text_note_body, self.e_text_note_body), + (TEXTNS, 'note-citation'):(None, self.e_text_note_citation), + (TEXTNS, 'notes-configuration'):(self.s_ignorexml, None), + (TEXTNS, 'object-index-source'):(self.s_text_x_source, self.e_text_x_source), (TEXTNS, 'p'): (self.s_text_p, self.e_text_p), (TEXTNS, 's'): (self.s_text_s, None), (TEXTNS, 'span'): (self.s_text_span, self.e_text_span), (TEXTNS, 'tab'): (self.s_text_tab, None), - (TEXTNS, "table-index-source"):(self.s_text_x_source, self.e_text_x_source), - (TEXTNS, "table-of-content-source"):(self.s_text_x_source, self.e_text_x_source), - (TEXTNS, "user-index-source"):(self.s_text_x_source, self.e_text_x_source), - (TEXTNS, "page-number"):(None, None), + (TEXTNS, 'table-index-source'):(self.s_text_x_source, self.e_text_x_source), + (TEXTNS, 'table-of-content-source'):(self.s_text_x_source, self.e_text_x_source), + (TEXTNS, 'user-index-source'):(self.s_text_x_source, self.e_text_x_source), + (TEXTNS, 'page-number'):(None, None), } diff --git a/src/odf/odfmanifest.py b/src/odf/odfmanifest.py index 14986e5c67..feeabd9ab5 100644 --- a/src/odf/odfmanifest.py +++ b/src/odf/odfmanifest.py @@ -25,7 +25,7 @@ import zipfile from xml.sax import handler, make_parser from xml.sax.xmlreader import InputSource -MANIFESTNS="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" +MANIFESTNS='urn:oasis:names:tc:opendocument:xmlns:manifest:1.0' # ----------------------------------------------------------------------------- # @@ -35,8 +35,8 @@ MANIFESTNS="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" class ODFManifestHandler(handler.ContentHandler): - """ The ODFManifestHandler parses a manifest file and produces a list of - content """ + ''' The ODFManifestHandler parses a manifest file and produces a list of + content ''' def __init__(self): self.manifest = {} @@ -77,7 +77,7 @@ class ODFManifestHandler(handler.ContentHandler): pass def s_file_entry(self, tag, attrs): - m = attrs.get((MANIFESTNS, 'media-type'),"application/octet-stream") + m = attrs.get((MANIFESTNS, 'media-type'),'application/octet-stream') p = attrs.get((MANIFESTNS, 'full-path')) self.manifest[p] = {'media-type':m, 'full-path':p} @@ -110,8 +110,8 @@ def odfmanifest(odtfile): return manifestlist(manifest) -if __name__ == "__main__": +if __name__ == '__main__': import sys result = odfmanifest(sys.argv[1]) for file in result.values(): - print("%-40s %-40s" % (file['media-type'], file['full-path'])) + print('%-40s %-40s' % (file['media-type'], file['full-path'])) diff --git a/src/odf/office.py b/src/odf/office.py index 41a9330404..1705bb3106 100644 --- a/src/odf/office.py +++ b/src/odf/office.py @@ -53,23 +53,23 @@ def DdeSource(**args): return Element(qname=(OFFICENS,'dde-source'), **args) -def Document(version="1.1", **args): +def Document(version='1.1', **args): return Element(qname=(OFFICENS,'document'), version=version, **args) -def DocumentContent(version="1.1", **args): +def DocumentContent(version='1.1', **args): return Element(qname=(OFFICENS, 'document-content'), version=version, **args) -def DocumentMeta(version="1.1", **args): +def DocumentMeta(version='1.1', **args): return Element(qname=(OFFICENS, 'document-meta'), version=version, **args) -def DocumentSettings(version="1.1", **args): +def DocumentSettings(version='1.1', **args): return Element(qname=(OFFICENS, 'document-settings'), version=version, **args) -def DocumentStyles(version="1.1", **args): +def DocumentStyles(version='1.1', **args): return Element(qname=(OFFICENS, 'document-styles'), version=version, **args) diff --git a/src/odf/opendocument.py b/src/odf/opendocument.py index 3bf3a4f25c..e804a93942 100644 --- a/src/odf/opendocument.py +++ b/src/odf/opendocument.py @@ -18,7 +18,7 @@ # -__doc__="""Use OpenDocument to generate your documents.""" +__doc__='''Use OpenDocument to generate your documents.''' import mimetypes import sys @@ -99,19 +99,19 @@ class OpaqueObject: class OpenDocument: - """ A class to hold the content of an OpenDocument document + ''' A class to hold the content of an OpenDocument document Use the xml method to write the XML source to the screen or to a file d = OpenDocument(mimetype) fd.write(d.xml()) - """ + ''' thumbnail = None def __init__(self, mimetype, add_generator=True): self.mimetype = mimetype self.childobjects = [] self._extra = [] - self.folder = "" # Always empty for toplevel documents + self.folder = '' # Always empty for toplevel documents self.topnode = Document(mimetype=self.mimetype) self.topnode.ownerDocument = self @@ -151,8 +151,8 @@ class OpenDocument: self._styles_ooo_fix = {} def build_caches(self, element): - """ Called from element.py - """ + ''' Called from element.py + ''' if element.qname not in self.element_dict: self.element_dict[element.qname] = [] self.element_dict[element.qname].append(element) @@ -191,9 +191,9 @@ class OpenDocument: f.close() def xml(self): - """ Generates the full document as an XML file + ''' Generates the full document as an XML file Always written as a bytestream in UTF-8 encoding - """ + ''' self.__replaceGenerator() xml=PolyglotBytesIO() xml.write(_XMLPROLOGUE) @@ -201,9 +201,9 @@ class OpenDocument: return xml.getvalue() def contentxml(self): - """ Generates the content.xml file + ''' Generates the content.xml file Always written as a bytestream in UTF-8 encoding - """ + ''' xml=PolyglotBytesIO() xml.write(_XMLPROLOGUE) x = DocumentContent() @@ -235,7 +235,7 @@ class OpenDocument: return xml.getvalue() def metaxml(self): - """ Generates the meta.xml file """ + ''' Generates the meta.xml file ''' self.__replaceGenerator() x = DocumentMeta() x.addElement(self.meta) @@ -245,7 +245,7 @@ class OpenDocument: return xml.getvalue() def settingsxml(self): - """ Generates the settings.xml file """ + ''' Generates the settings.xml file ''' x = DocumentSettings() x.addElement(self.settings) xml=PolyglotStringIO() @@ -254,10 +254,10 @@ class OpenDocument: return xml.getvalue() def _parseoneelement(self, top, stylenamelist): - """ Finds references to style objects in master-styles + ''' Finds references to style objects in master-styles and add the style name to the style list if not already there. Recursive - """ + ''' for e in top.childNodes: if e.nodeType == element.Node.ELEMENT_NODE: for styleref in ( @@ -280,10 +280,10 @@ class OpenDocument: return stylenamelist def _used_auto_styles(self, segments): - """ Loop through the masterstyles elements, and find the automatic + ''' Loop through the masterstyles elements, and find the automatic styles that are used. These will be added to the automatic-styles element in styles.xml - """ + ''' stylenamelist = [] for top in segments: stylenamelist = self._parseoneelement(top, stylenamelist) @@ -294,7 +294,7 @@ class OpenDocument: return stylelist def stylesxml(self): - """ Generates the styles.xml file """ + ''' Generates the styles.xml file ''' xml=PolyglotStringIO() xml.write(_XMLPROLOGUE) x = DocumentStyles() @@ -329,7 +329,7 @@ class OpenDocument: ext='' else: ext = mimetypes.guess_extension(mediatype) - manifestfn = f"Pictures/{(time.time()*10000000000):0.0f}{ext}" + manifestfn = f'Pictures/{(time.time()*10000000000):0.0f}{ext}' self.Pictures[manifestfn] = (IS_FILENAME, filename, mediatype) else: manifestfn = filename @@ -353,7 +353,7 @@ class OpenDocument: ext='' else: ext = mimetypes.guess_extension(mediatype) - manifestfn = f"Pictures/{(time.time()*10000000000):0.0f}{ext}" + manifestfn = f'Pictures/{(time.time()*10000000000):0.0f}{ext}' self.Pictures[manifestfn] = (IS_FILENAME, filename, mediatype) return manifestfn @@ -365,14 +365,14 @@ class OpenDocument: indicates the image format. """ ext = mimetypes.guess_extension(mediatype) - manifestfn = f"Pictures/{(time.time()*10000000000):0.0f}{ext}" + manifestfn = f'Pictures/{(time.time()*10000000000):0.0f}{ext}' self.Pictures[manifestfn] = (IS_IMAGE, content, mediatype) return manifestfn def addThumbnail(self, filecontent=None): - """ Add a fixed thumbnail + ''' Add a fixed thumbnail The thumbnail in the library is big, so this is pretty useless. - """ + ''' if filecontent is None: import thumbnail self.thumbnail = thumbnail.thumbnail() @@ -380,20 +380,20 @@ class OpenDocument: self.thumbnail = filecontent def addObject(self, document, objectname=None): - """ Adds an object (subdocument). The object must be an OpenDocument class + ''' Adds an object (subdocument). The object must be an OpenDocument class The return value will be the folder in the zipfile the object is stored in - """ + ''' self.childobjects.append(document) if objectname is None: - document.folder = "%s/Object %d" % (self.folder, len(self.childobjects)) + document.folder = '%s/Object %d' % (self.folder, len(self.childobjects)) else: document.folder = objectname - return ".%s" % document.folder + return '.%s' % document.folder def _savePictures(self, object, folder): for arcname, picturerec in object.Pictures.items(): what_it_is, fileobj, mediatype = picturerec - self.manifest.addElement(manifest.FileEntry(fullpath=f"{folder}{arcname}", mediatype=mediatype)) + self.manifest.addElement(manifest.FileEntry(fullpath=f'{folder}{arcname}', mediatype=mediatype)) if what_it_is == IS_FILENAME: self._z.write(fileobj, arcname, zipfile.ZIP_STORED) else: @@ -411,9 +411,9 @@ class OpenDocument: subobjectnum += 1 def __replaceGenerator(self): - """ Section 3.1.1: The application MUST NOT export the original identifier + ''' Section 3.1.1: The application MUST NOT export the original identifier belonging to the application that created the document. - """ + ''' for m in self.meta.childNodes[:]: if m.qname == (METANS, 'generator'): self.meta.removeChild(m) @@ -424,25 +424,25 @@ class OpenDocument: If the filename is '-' then save to stdout """ if outputfile == '-': - outputfp = zipfile.ZipFile(sys.stdout,"w") + outputfp = zipfile.ZipFile(sys.stdout,'w') else: if addsuffix: outputfile = outputfile + odmimetypes.get(self.mimetype,'.xxx') - outputfp = zipfile.ZipFile(outputfile, "w") + outputfp = zipfile.ZipFile(outputfile, 'w') self.__zipwrite(outputfp) outputfp.close() def write(self, outputfp): - """ User API to write the ODF file to an open file descriptor + ''' User API to write the ODF file to an open file descriptor Writes the ZIP format - """ - zipoutputfp = zipfile.ZipFile(outputfp,"w") + ''' + zipoutputfp = zipfile.ZipFile(outputfp,'w') self.__zipwrite(zipoutputfp) def __zipwrite(self, outputfp): - """ Write the document to an open file pointer + ''' Write the document to an open file pointer This is where the real work is done - """ + ''' self._z = outputfp self._now = time.localtime()[:6] self.manifest = manifest.Manifest() @@ -453,23 +453,23 @@ class OpenDocument: zi.external_attr = UNIXPERMS self._z.writestr(zi, self.mimetype) - self._saveXmlObjects(self,"") + self._saveXmlObjects(self,'') # Write pictures - self._savePictures(self,"") + self._savePictures(self,'') # Write the thumbnail if self.thumbnail is not None: - self.manifest.addElement(manifest.FileEntry(fullpath="Thumbnails/", mediatype='')) - self.manifest.addElement(manifest.FileEntry(fullpath="Thumbnails/thumbnail.png", mediatype='')) - zi = zipfile.ZipInfo("Thumbnails/thumbnail.png", self._now) + self.manifest.addElement(manifest.FileEntry(fullpath='Thumbnails/', mediatype='')) + self.manifest.addElement(manifest.FileEntry(fullpath='Thumbnails/thumbnail.png', mediatype='')) + zi = zipfile.ZipInfo('Thumbnails/thumbnail.png', self._now) zi.compress_type = zipfile.ZIP_DEFLATED zi.external_attr = UNIXPERMS self._z.writestr(zi, self.thumbnail) # Write any extra files for op in self._extra: - if op.filename == "META-INF/documentsignatures.xml": + if op.filename == 'META-INF/documentsignatures.xml': continue # Don't save signatures self.manifest.addElement(manifest.FileEntry(fullpath=op.filename, mediatype=op.mediatype)) zi = zipfile.ZipInfo(op.filename.encode('utf-8'), self._now) @@ -478,7 +478,7 @@ class OpenDocument: if op.content is not None: self._z.writestr(zi, op.content) # Write manifest - zi = zipfile.ZipInfo("META-INF/manifest.xml", self._now) + zi = zipfile.ZipInfo('META-INF/manifest.xml', self._now) zi.compress_type = zipfile.ZIP_DEFLATED zi.external_attr = UNIXPERMS self._z.writestr(zi, self.__manifestxml()) @@ -488,35 +488,35 @@ class OpenDocument: def _saveXmlObjects(self, object, folder): if self == object: - self.manifest.addElement(manifest.FileEntry(fullpath="/", mediatype=object.mimetype)) + self.manifest.addElement(manifest.FileEntry(fullpath='/', mediatype=object.mimetype)) else: self.manifest.addElement(manifest.FileEntry(fullpath=folder, mediatype=object.mimetype)) # Write styles - self.manifest.addElement(manifest.FileEntry(fullpath="%sstyles.xml" % folder, mediatype="text/xml")) - zi = zipfile.ZipInfo("%sstyles.xml" % folder, self._now) + self.manifest.addElement(manifest.FileEntry(fullpath='%sstyles.xml' % folder, mediatype='text/xml')) + zi = zipfile.ZipInfo('%sstyles.xml' % folder, self._now) zi.compress_type = zipfile.ZIP_DEFLATED zi.external_attr = UNIXPERMS self._z.writestr(zi, object.stylesxml()) # Write content - self.manifest.addElement(manifest.FileEntry(fullpath="%scontent.xml" % folder, mediatype="text/xml")) - zi = zipfile.ZipInfo("%scontent.xml" % folder, self._now) + self.manifest.addElement(manifest.FileEntry(fullpath='%scontent.xml' % folder, mediatype='text/xml')) + zi = zipfile.ZipInfo('%scontent.xml' % folder, self._now) zi.compress_type = zipfile.ZIP_DEFLATED zi.external_attr = UNIXPERMS self._z.writestr(zi, object.contentxml()) # Write settings if object.settings.hasChildNodes(): - self.manifest.addElement(manifest.FileEntry(fullpath="%ssettings.xml" % folder, mediatype="text/xml")) - zi = zipfile.ZipInfo("%ssettings.xml" % folder, self._now) + self.manifest.addElement(manifest.FileEntry(fullpath='%ssettings.xml' % folder, mediatype='text/xml')) + zi = zipfile.ZipInfo('%ssettings.xml' % folder, self._now) zi.compress_type = zipfile.ZIP_DEFLATED zi.external_attr = UNIXPERMS self._z.writestr(zi, object.settingsxml()) # Write meta if self == object: - self.manifest.addElement(manifest.FileEntry(fullpath="meta.xml", mediatype="text/xml")) - zi = zipfile.ZipInfo("meta.xml", self._now) + self.manifest.addElement(manifest.FileEntry(fullpath='meta.xml', mediatype='text/xml')) + zi = zipfile.ZipInfo('meta.xml', self._now) zi.compress_type = zipfile.ZIP_DEFLATED zi.external_attr = UNIXPERMS self._z.writestr(zi, object.metaxml()) @@ -535,26 +535,26 @@ class OpenDocument: return element(check_grammar=False) def createTextNode(self, data): - """ Method to create a text node """ + ''' Method to create a text node ''' return element.Text(data) def createCDATASection(self, data): - """ Method to create a CDATA section """ + ''' Method to create a CDATA section ''' return element.CDATASection(data) def getMediaType(self): - """ Returns the media type """ + ''' Returns the media type ''' return self.mimetype def getStyleByName(self, name): - """ Finds a style object based on the name """ + ''' Finds a style object based on the name ''' ncname = make_NCName(name) if self._styles_dict == {}: self.rebuild_caches() return self._styles_dict.get(ncname, None) def getElementsByType(self, element): - """ Gets elements based on the type, which is function from text.py, draw.py etc. """ + ''' Gets elements based on the type, which is function from text.py, draw.py etc. ''' obj = element(check_grammar=False) if self.element_dict == {}: self.rebuild_caches() @@ -564,7 +564,7 @@ class OpenDocument: def OpenDocumentChart(): - """ Creates a chart document """ + ''' Creates a chart document ''' doc = OpenDocument('application/vnd.oasis.opendocument.chart') doc.chart = Chart() doc.body.addElement(doc.chart) @@ -572,7 +572,7 @@ def OpenDocumentChart(): def OpenDocumentDrawing(): - """ Creates a drawing document """ + ''' Creates a drawing document ''' doc = OpenDocument('application/vnd.oasis.opendocument.graphics') doc.drawing = Drawing() doc.body.addElement(doc.drawing) @@ -580,7 +580,7 @@ def OpenDocumentDrawing(): def OpenDocumentImage(): - """ Creates an image document """ + ''' Creates an image document ''' doc = OpenDocument('application/vnd.oasis.opendocument.image') doc.image = Image() doc.body.addElement(doc.image) @@ -588,7 +588,7 @@ def OpenDocumentImage(): def OpenDocumentPresentation(): - """ Creates a presentation document """ + ''' Creates a presentation document ''' doc = OpenDocument('application/vnd.oasis.opendocument.presentation') doc.presentation = Presentation() doc.body.addElement(doc.presentation) @@ -596,7 +596,7 @@ def OpenDocumentPresentation(): def OpenDocumentSpreadsheet(): - """ Creates a spreadsheet document """ + ''' Creates a spreadsheet document ''' doc = OpenDocument('application/vnd.oasis.opendocument.spreadsheet') doc.spreadsheet = Spreadsheet() doc.body.addElement(doc.spreadsheet) @@ -604,7 +604,7 @@ def OpenDocumentSpreadsheet(): def OpenDocumentText(): - """ Creates a text document """ + ''' Creates a text document ''' doc = OpenDocument('application/vnd.oasis.opendocument.text') doc.text = Text() doc.body.addElement(doc.text) @@ -612,7 +612,7 @@ def OpenDocumentText(): def OpenDocumentTextMaster(): - """ Creates a text master document """ + ''' Creates a text master document ''' doc = OpenDocument('application/vnd.oasis.opendocument.text-master') doc.text = Text() doc.body.addElement(doc.text) @@ -646,9 +646,9 @@ def __loadxmlparts(z, manifest, doc, objectpath): def load(odffile): - """ Load an ODF file into memory + ''' Load an ODF file into memory Returns a reference to the structure - """ + ''' z = zipfile.ZipFile(odffile) try: mimetype = z.read('mimetype') @@ -661,18 +661,18 @@ def load(odffile): manifest = manifestlist(manifestpart) __loadxmlparts(z, manifest, doc, '') for mentry,mvalue in manifest.items(): - if mentry[:9] == "Pictures/" and len(mentry) > 9: + if mentry[:9] == 'Pictures/' and len(mentry) > 9: doc.addPicture(mvalue['full-path'], mvalue['media-type'], z.read(mentry)) - elif mentry == "Thumbnails/thumbnail.png": + elif mentry == 'Thumbnails/thumbnail.png': doc.addThumbnail(z.read(mentry)) elif mentry in ('settings.xml', 'meta.xml', 'content.xml', 'styles.xml'): pass # Load subobjects into structure - elif mentry[:7] == "Object " and len(mentry) < 11 and mentry[-1] == "/": + elif mentry[:7] == 'Object ' and len(mentry) < 11 and mentry[-1] == '/': subdoc = OpenDocument(mvalue['media-type'], add_generator=False) - doc.addObject(subdoc, "/" + mentry[:-1]) + doc.addObject(subdoc, '/' + mentry[:-1]) __loadxmlparts(z, manifest, subdoc, mentry) - elif mentry[:7] == "Object ": + elif mentry[:7] == 'Object ': pass # Don't load subobjects as opaque objects else: if mvalue['full-path'][-1] == '/': diff --git a/src/odf/teletype.py b/src/odf/teletype.py index 0384bc734b..182962e3fa 100644 --- a/src/odf/teletype.py +++ b/src/odf/teletype.py @@ -83,17 +83,17 @@ class WhitespaceText: self._emitTextBuffer(odfElement) def _emitTextBuffer(self, odfElement): - """ Creates a Text Node whose contents are the current textBuffer. + ''' Creates a Text Node whose contents are the current textBuffer. Side effect: clears the text buffer. - """ + ''' if len(self.textBuffer) > 0: odfElement.addText(''.join(self.textBuffer)) self.textBuffer = [] def _emitSpaces(self, odfElement): - """ Creates a element for the current spaceCount. + ''' Creates a element for the current spaceCount. Side effect: sets spaceCount back to zero - """ + ''' if self.spaceCount > 0: spaceElement = S(c=self.spaceCount) odfElement.addElement(spaceElement) @@ -106,12 +106,12 @@ def addTextToElement(odfElement, s): def extractText(odfElement): - """ Extract text content from an Element, with whitespace represented + ''' Extract text content from an Element, with whitespace represented properly. Returns the text, with tabs, spaces, and newlines correctly evaluated. This method recursively descends through the children of the given element, accumulating text and "unwrapping" , , and elements along the way. - """ + ''' result = [] if len(odfElement.childNodes) != 0: @@ -121,18 +121,18 @@ def extractText(odfElement): elif child.nodeType == Node.ELEMENT_NODE: subElement = child tagName = subElement.qname - if tagName == ("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "line-break"): - result.append("\n") - elif tagName == ("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "tab"): - result.append("\t") - elif tagName == ("urn:oasis:names:tc:opendocument:xmlns:text:1.0", "s"): + if tagName == ('urn:oasis:names:tc:opendocument:xmlns:text:1.0', 'line-break'): + result.append('\n') + elif tagName == ('urn:oasis:names:tc:opendocument:xmlns:text:1.0', 'tab'): + result.append('\t') + elif tagName == ('urn:oasis:names:tc:opendocument:xmlns:text:1.0', 's'): c = subElement.getAttribute('c') if c: spaceCount = int(c) else: spaceCount = 1 - result.append(" " * spaceCount) + result.append(' ' * spaceCount) else: result.append(extractText(subElement)) return ''.join(result) diff --git a/src/odf/thumbnail.py b/src/odf/thumbnail.py index b2c65e03c2..e41362c304 100644 --- a/src/odf/thumbnail.py +++ b/src/odf/thumbnail.py @@ -4,7 +4,7 @@ # License: GPL-3 import base64 -iconstr = b""" +iconstr = b''' iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAA3NCSVQICAjb4U/gAAAACXBIWXMA AA3XAAAN1wFCKJt4AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAspQTFRF AAAAgICAZmZmVVVVgICAampqgICAYmJidnZ2aGhoa2treHh4ZGRkcHBwdnZ2ampqcXFxb29vc3Nz @@ -48,7 +48,7 @@ BiRAqkN0UJpQckxaspHRj/IDhLE4KiAhJKLOko01SEYlmwSSY9OSjX1FYEo2RIpQrZDQZWdxSjZW UnLjko1lIrCOKLnkGFuysQfJpGRDQUTMko2dpMS0ZGM5OUaWbJJKjg1KNnaIMC3ZkJeMzEs21pZI 5QEJcc0CH5TSZWdSAJ/96wq6H9jGaPO3R1IAo3Q9RwkoSwG8AdTT64YittdSAEVLL6r5iV9JCqB8 9Dg1/e525oN8FrzYm21ctJGEBL49KQ6VKvpn/wE4qQRdyiC8rQAAAABJRU5ErkJggg== -""" +''' def thumbnail(): @@ -56,8 +56,8 @@ def thumbnail(): return icon -if __name__ == "__main__": +if __name__ == '__main__': icon = thumbnail() - f = open("thumbnail.png","wb") + f = open('thumbnail.png','wb') f.write(icon) f.close() diff --git a/src/odf/userfield.py b/src/odf/userfield.py index d9013e973b..bf4e5878ca 100644 --- a/src/odf/userfield.py +++ b/src/odf/userfield.py @@ -18,7 +18,7 @@ # # $Id: userfield.py 447 2008-07-10 20:01:30Z roug $ -"""Class to show and manipulate user fields in odf documents.""" +'''Class to show and manipulate user fields in odf documents.''' import sys @@ -28,7 +28,7 @@ from odf.namespaces import OFFICENS from odf.opendocument import load from odf.text import UserFieldDecl -OUTENCODING = "utf-8" +OUTENCODING = 'utf-8' # OpenDocument v.1.0 section 6.7.1 @@ -44,19 +44,19 @@ VALUE_TYPES = { class UserFields: - """List, view and manipulate user fields.""" + '''List, view and manipulate user fields.''' # these attributes can be a filename or a file like object src_file = None dest_file = None def __init__(self, src=None, dest=None): - """Constructor + '''Constructor src ... source document name, file like object or None for stdin dest ... destination document name, file like object or None for stdout - """ + ''' self.src_file = src self.dest_file = dest self.document = None @@ -65,7 +65,7 @@ class UserFields: if isinstance(self.src_file, (bytes, str)): # src_file is a filename, check if it is a zip-file if not zipfile.is_zipfile(self.src_file): - raise TypeError("%s is no odt file." % self.src_file) + raise TypeError('%s is no odt file.' % self.src_file) elif self.src_file is None: # use stdin if no file given self.src_file = sys.stdin @@ -81,21 +81,21 @@ class UserFields: self.document.save(self.dest_file) def list_fields(self): - """List (extract) all known user-fields. + '''List (extract) all known user-fields. Returns list of user-field names. - """ + ''' return [x[0] for x in self.list_fields_and_values()] def list_fields_and_values(self, field_names=None): - """List (extract) user-fields with type and value. + '''List (extract) user-fields with type and value. field_names ... list of field names to show or None for all. Returns list of tuples (, , ). - """ + ''' self.loaddoc() found_fields = [] all_fields = self.document.getElementsByType(UserFieldDecl) @@ -114,32 +114,32 @@ class UserFields: return found_fields def list_values(self, field_names): - """Extract the contents of given field names from the file. + '''Extract the contents of given field names from the file. field_names ... list of field names Returns list of field values. - """ + ''' return [x[2] for x in self.list_fields_and_values(field_names)] def get(self, field_name): - """Extract the contents of this field from the file. + '''Extract the contents of this field from the file. Returns field value or None if field does not exist. - """ + ''' values = self.list_values([field_name]) if not values: return None return values[0] def get_type_and_value(self, field_name): - """Extract the type and contents of this field from the file. + '''Extract the type and contents of this field from the file. Returns tuple (, ) or None if field does not exist. - """ + ''' fields = self.list_fields_and_values([field_name]) if not fields: return None @@ -147,13 +147,13 @@ class UserFields: return value_type, value def update(self, data): - """Set the value of user fields. The field types will be the same. + '''Set the value of user fields. The field types will be the same. data ... dict, with field name as key, field value as value Returns None - """ + ''' self.loaddoc() all_fields = self.document.getElementsByType(UserFieldDecl) for f in all_fields: diff --git a/src/qt/__init__.py b/src/qt/__init__.py index 4366f1400b..d55b913aa6 100644 --- a/src/qt/__init__.py +++ b/src/qt/__init__.py @@ -19,5 +19,5 @@ top_level_module_names = ('QtCore', def __getattr__(name): if name in top_level_module_names: import importlib - return importlib.import_module("PyQt6." + name) + return importlib.import_module('PyQt6.' + name) raise AttributeError(name)