#!/usr/bin/env perl # pyimportcan.pl -- Python imports canonical format filter # Copyright (C) 2013-2024 Sergey Matveev (stargrave@stargrave.org) # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . =pod =head1 PROBLEM Python allows various different formats of how to make importing of modules and functions: from foo import bar from foo import bar, baz from foo import ( bar, baz ) from foo import ( baz, bar, ) from foo import bar, \ baz Everything can be unsorted at any place, thus making the task of checking if import already exists rather hard. Moreover it complicates just adding another import, because you have to find it first and deal with the format it is already written. Everything can use different indentation practices. Everything can be doubled and you won't notice it: from foo import bar [many lines] from foo import ( baz, bar ) Allowing multiple imports on one line leads to possibility of long lines appearing. The whole import sections with various formats just can not be technically sorted with the editor (no way to do "vip:sort" in Vim). Merge conflict in the imports section can be not so trivial to resolve, however in most cases just aggregating them will be enough. If you want to find the code that imports bar from foo, then you can do it with "from foo import bar", but can not with "import (\n" is most cases. You are forced to use special Python-related software to deal with it, but no grep or sed. =head1 SOLUTION Use the very convenient simple single format. Single import per line, period. Sorted and only identical ones. With git merge conflict lines removed. This script reads stdin with import-lines and writes canonical representation of them to stdout. For example you can select lines in Vim and just enter ":!pyimportcan.pl". From that kind of imports: from waaccounts.models import OrgRole import math from waaccounts.models import UserAccount from waadv.filters import AdvCampaignFilterSet as foo1 from waadv.filters import AdvCampaignBidFilterSet from waadv.filters import AdvCampaignUniqueShowFilterSet as foo2 from waadv.filters import VasttaskFilterSet from waadv.forms import AdvCampaignCreateForm from waadv.forms import PriceListTotalBudgetDiscountFormset from waadv.models import Adv from waadv.models import PriceListTotalBudgetDiscount from waadv.models import VastTask from wacontent.models import VrContractGroup from wacatalog.models import AdvGroup from wahelpers.counters import adv_campaign_click_counter_loader from wahelpers.counters import adv_video_view_fact_counter_loader from wahelpers.counters import adv_view_percent from wahelpers.counters import ctr from wahelpers.functions import get_json_rpc_proxy, get_return_url <<<<<<< HEAD from waorigin.kendo_menus.toolbar import KendoGridToolbar from waorigin.kendo_menus.toolbar_button import KendoToolbarButton ======= from http_utils import JSONResponse from wahelpers.counters import ( adv_campaign_click_counter_loader, adv_campaign_click_fact_counter_loader, ctr, adv_view_percent, ) from waorigin.kendo_menus.toolbar_button import ( AddKendoToolbarButton, StatusKendoToolbarButton ) from waorigin.models import Content, ContentCategory3 as foo4, ContentCategory2, Files from waorigin.views import filter_view, model_autocompletion, \ create_update_with_auto_user, get_objects_for_edit_many from wapartners.models import PrNomenclature, PrSite >>>>>>> 1bf0b64... fixed view refs #11992 from waorigin.kendo_data_source import KendoDataSourceView from waorigin.models import Content from waorigin.models import ContentCategory2 from waorigin.models import ContentCategory3 from waorigin.models import Files from waorigin.views import create_update_with_auto_user from waorigin.views import filter_view from waorigin.views import get_objects_for_edit_many from waorigin.views import model_autocompletion from wapartners.models import PrSite from wapartners.models import PrNomenclature it makes the following one (by feeding it to stdin and capturing on stdout): import math from http_utils import JSONResponse from waaccounts.models import OrgRole from waaccounts.models import UserAccount from waadv.filters import AdvCampaignBidFilterSet from waadv.filters import AdvCampaignFilterSet as foo1 from waadv.filters import AdvCampaignUniqueShowFilterSet as foo2 from waadv.filters import VasttaskFilterSet from waadv.forms import AdvCampaignCreateForm from waadv.forms import PriceListTotalBudgetDiscountFormset from waadv.models import Adv from waadv.models import PriceListTotalBudgetDiscount from waadv.models import VastTask from wacatalog.models import AdvGroup from wacontent.models import VrContractGroup from wahelpers.counters import adv_campaign_click_counter_loader from wahelpers.counters import adv_campaign_click_fact_counter_loader from wahelpers.counters import adv_video_view_fact_counter_loader from wahelpers.counters import adv_view_percent from wahelpers.counters import ctr from wahelpers.functions import get_json_rpc_proxy from wahelpers.functions import get_return_url from waorigin.kendo_data_source import KendoDataSourceView from waorigin.kendo_menus.toolbar import KendoGridToolbar from waorigin.kendo_menus.toolbar_button import AddKendoToolbarButton from waorigin.kendo_menus.toolbar_button import KendoToolbarButton from waorigin.kendo_menus.toolbar_button import StatusKendoToolbarButton from waorigin.models import Content from waorigin.models import ContentCategory2 from waorigin.models import ContentCategory3 from waorigin.models import ContentCategory3 as foo4 from waorigin.models import Files from waorigin.views import create_update_with_auto_user from waorigin.views import filter_view from waorigin.views import get_objects_for_edit_many from waorigin.views import model_autocompletion from wapartners.models import PrNomenclature from wapartners.models import PrSite =head1 AUTHOR Sergey Matveev L =cut use strict; use warnings; my $buf; my $con; my @imports; my %parsed; # Collect strings and aggregate the splitted ones while(<>){ next if /^[<=>]{2,}/; next if /^\s*#/; chop; $buf = ($con ? $buf : "") . $_; $con = /[\\,\(]\s*$/ ? 1 : 0; next if /^\s*\)*\s*$/; push @imports, $buf; }; # Consolidate information from where what is imported foreach (@imports) { s/[\\\(\)]//g; s/ */ /g; next if /import\s*$/; /^(.*)\s*import\s*(.*)$/; my ($where, $what) = ($1, $2); map { $parsed{$where}->{$_}++ } split /\s*,\s*/, $what; }; foreach my $where (sort keys %parsed){ map { print $where . "import $_\n" } sort keys %{$parsed{$where}}; };