PNG  IHDR pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F@8N ' p @8N@8}' p '#@8N@8N pQ9p!i~}|6-ӪG` VP.@*j>[ K^<֐Z]@8N'KQ<Q(`s" 'hgpKB`R@Dqj '  'P$a ( `D$Na L?u80e J,K˷NI'0eݷ(NI'؀ 2ipIIKp`:O'`ʤxB8Ѥx Ѥx $ $P6 :vRNb 'p,>NB 'P]-->P T+*^h& p '‰a ‰ (ĵt#u33;Nt̵'ޯ; [3W ~]0KH1q@8]O2]3*̧7# *p>us p _6]/}-4|t'|Smx= DoʾM×M_8!)6lq':l7!|4} '\ne t!=hnLn (~Dn\+‰_4k)0e@OhZ`F `.m1} 'vp{F`ON7Srx 'D˸nV`><;yMx!IS钦OM)Ե٥x 'DSD6bS8!" ODz#R >S8!7ّxEh0m$MIPHi$IvS8IN$I p$O8I,sk&I)$IN$Hi$I^Ah.p$MIN$IR8I·N "IF9Ah0m$MIN$IR8IN$I 3jIU;kO$ɳN$+ q.x* tEXtComment

Viewing File: /opt/cloudlinux/venv/lib/python3.11/site-packages/pylint_django/checkers/migrations.py

# Copyright (c) 2018, 2020 Alexander Todorov <atodorov@MrSenko.com>
# Copyright (c) 2020 Bryan Mutai <mutaiwork@gmail.com>

# Licensed under the GPL 2.0: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/PyCQA/pylint-django/blob/master/LICENSE
"""
Various suggestions around migrations. Disabled by default! Enable with
pylint --load-plugins=pylint_django.checkers.migrations
"""

import astroid
from pylint import checkers, interfaces
from pylint.checkers import utils
from pylint_plugin_utils import suppress_message

from pylint_django import compat
from pylint_django.__pkginfo__ import BASE_ID
from pylint_django.utils import is_migrations_module


def _is_addfield_with_default(call):
    if not isinstance(call.func, astroid.Attribute):
        return False

    if not call.func.attrname == "AddField":
        return False

    for keyword in call.keywords:
        # looking for AddField(..., field=XXX(..., default=Y, ...), ...)
        if keyword.arg == "field" and isinstance(keyword.value, astroid.Call):
            # loop over XXX's keywords
            # NOTE: not checking if XXX is an actual field type because there could
            # be many types we're not aware of. Also the migration will probably break
            # if XXX doesn't instantiate a field object!
            for field_keyword in keyword.value.keywords:
                if field_keyword.arg == "default":
                    return True

    return False


class NewDbFieldWithDefaultChecker(checkers.BaseChecker):
    """
    Looks for migrations which add new model fields and these fields have a
    default value. According to Django docs this may have performance penalties
    especially on large tables:
    https://docs.djangoproject.com/en/2.0/topics/migrations/#postgresql

    The preferred way is to add a new DB column with null=True because it will
    be created instantly and then possibly populate the table with the
    desired default values.
    """

    __implements__ = (interfaces.IAstroidChecker,)

    # configuration section name
    name = "new-db-field-with-default"
    msgs = {
        f"W{BASE_ID}98": (
            "%s AddField with default value",
            "new-db-field-with-default",
            "Used when Pylint detects migrations adding new fields with a default value.",
        )
    }

    _migration_modules = []
    _possible_offences = {}

    def visit_module(self, node):
        if is_migrations_module(node):
            self._migration_modules.append(node)

    def visit_call(self, node):
        try:
            module = node.frame().parent
        except:  # noqa: E722, pylint: disable=bare-except
            return

        if not is_migrations_module(module):
            return

        if _is_addfield_with_default(node):
            if module not in self._possible_offences:
                self._possible_offences[module] = []

            if node not in self._possible_offences[module]:
                self._possible_offences[module].append(node)

    @utils.check_messages("new-db-field-with-default")
    def close(self):
        def _path(node):
            return node.path

        # sort all migrations by name in reverse order b/c
        # we need only the latest ones
        self._migration_modules.sort(key=_path, reverse=True)

        # filter out the last migration modules under each distinct
        # migrations directory, iow leave only the latest migrations
        # for each application
        last_name_space = ""
        latest_migrations = []
        for module in self._migration_modules:
            name_space = module.path[0].split("migrations")[0]
            if name_space != last_name_space:
                last_name_space = name_space
                latest_migrations.append(module)

        for module, nodes in self._possible_offences.items():
            if module in latest_migrations:
                for node in nodes:
                    self.add_message("new-db-field-with-default", args=module.name, node=node)


class MissingBackwardsMigrationChecker(checkers.BaseChecker):
    __implements__ = (interfaces.IAstroidChecker,)

    name = "missing-backwards-migration-callable"

    msgs = {
        f"W{BASE_ID}97": (
            "Always include backwards migration callable",
            "missing-backwards-migration-callable",
            "Always include a backwards/reverse callable counterpart so that the migration is not irreversable.",
        )
    }

    @utils.check_messages("missing-backwards-migration-callable")
    def visit_call(self, node):
        try:
            module = node.frame().parent
        except:  # noqa: E722, pylint: disable=bare-except
            return

        if not is_migrations_module(module):
            return

        if node.func.as_string().endswith("RunPython") and len(node.args) < 2:
            if node.keywords:
                for keyword in node.keywords:
                    if keyword.arg == "reverse_code":
                        return
                self.add_message("missing-backwards-migration-callable", node=node)
            else:
                self.add_message("missing-backwards-migration-callable", node=node)


def is_in_migrations(node):
    """
    RunPython() migrations receive forward/backwards functions with signature:

        def func(apps, schema_editor):

    which could be unused. This augmentation will suppress all 'unused-argument'
    messages coming from functions in migration modules.
    """
    return is_migrations_module(node.parent)


def load_configuration(linter):  # TODO this is redundant and can be  removed
    # don't blacklist migrations for this checker
    new_black_list = list(linter.config.black_list)
    if "migrations" in new_black_list:
        new_black_list.remove("migrations")
    linter.config.black_list = new_black_list


def register(linter):
    """Required method to auto register this checker."""
    linter.register_checker(NewDbFieldWithDefaultChecker(linter))
    linter.register_checker(MissingBackwardsMigrationChecker(linter))
    if not compat.LOAD_CONFIGURATION_SUPPORTED:
        load_configuration(linter)

    # apply augmentations for migration checkers
    # Unused arguments for migrations
    suppress_message(
        linter,
        checkers.variables.VariablesChecker.leave_functiondef,
        "unused-argument",
        is_in_migrations,
    )
Back to Directory=ceiIENDB`