
    ]jl                    p   d dl mZ d dlZd dlZd dlZd dlZd dlZd dlZd dlm	Z	m
Z
 d dlmZmZ d dlmZmZ d dlmZ d dlmZ d dlmZmZmZ d d	lmZmZmZ d d
lmZ ddlm Z  ddl!m"Z"m#Z#m$Z$ ddl%m&Z& ddl'm(Z(m)Z) ddl*m+Z+ ddl,m-Z-m.Z.m/Z/  ej`                  d      Z1e
 G d de	             Z2 G d d      Z3y)    )annotationsN)Enumunique)PIPEPopen)CallableIterable)settings)call_command)DEFAULT_DB_ALIASProgrammingErrorconnections)	Migration	RunPythonRunSQL)	Operation   )Cache)DEFAULT_CACHE_PATHDJANGO_APPS_WITH_MIGRATIONSEXPECTED_DATA_MIGRATION_ARGS)IgnoreMigration)analyse_sql_statementsget_sql_analyser_class)Issue)clean_bytes_to_strget_migration_abspathsplit_migration_pathdjango_migration_linterc                  .    e Zd ZdZdZdZdZedd       Zy)MessageTypeokignorewarningerrorc                 6    t        t        d t                    S )Nc                    | j                   S N)value)cs    R/root/env/lib/python3.12/site-packages/django_migration_linter/migration_linter.py<lambda>z$MessageType.values.<locals>.<lambda>*   s
    !''     )listmapr!    r-   r+   valueszMessageType.values(   s    C);788r-   N)return	list[str])	__name__
__module____qualname__OKIGNOREWARNINGERRORstaticmethodr1   r0   r-   r+   r!   r!   !   s(    	BFGE9 9r-   r!   c                     e Zd Zdddddddeedddddddddddf	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZddZddZ	 	 	 	 d	 	 	 	 	 	 	 	 	 ddZd dZ	e
d!d       Z	 	 	 	 	 	 	 	 d"d	Z	 	 	 	 	 	 	 	 	 	 d#d
Zd$dZd%dZddZedd       Zd&dZe
d'd       Ze	 	 	 	 d(d       Z	 d)	 	 	 	 	 d*dZ	 d)	 	 	 d+dZ	 	 d,	 	 	 	 	 	 	 	 	 d-dZ	 	 	 	 d.dZe
d        Z	 	 	 	 d/dZe
d0d       Ze
d0d       Z	 	 	 	 d1dZy)2MigrationLinterNFc                .   || _         || _        |xs g | _        || _        |xs g | _        || _        || _        |xs g | _        |xs t        | _	        |	xs t        | _        |
| _        || _        || _        |xs g | _        || _        || _        || _        t'        t(        j*                  | j                     d   |      | _        || _        || _        | j3                          | j5                         rzt7        | j                   | j                  | j                        | _        t7        | j                   | j                  | j                        | _        | j8                  j=                          ddlm }  |tB        | j                     d      | _"        y )NENGINE)analyser_stringr   )MigrationLoaderT)
connectionload)#django_pathignore_name_containsignore_nameinclude_name_containsinclude_nameinclude_appsexclude_appsexclude_migration_testsr   databaser   
cache_pathno_cacheonly_applied_migrationsonly_unapplied_migrationsquietwarnings_as_errors_testsall_warnings_as_errors	no_outputr   r
   	DATABASESsql_analyser_classignore_sqlmigrate_errorsignore_initial_migrationsreset_countersshould_use_cacher   	old_cache	new_cacherC   django.db.migrations.loaderrA   r   migration_loader)selfpathrE   rF   rG   rH   rI   rJ   rL   rM   rN   rO   rP   rK   rQ   rR   rS   rT   r@   rW   rX   rA   s                         r+   __init__zMigrationLinter.__init__.   si   0  $8!&,"%:"(.B(('>'D"$ 4$4$:(: '>$)B&[b
(@%&<#""8t}}-h7+#
 )A%)B& 	   ""4#3#3T]]DOOTDN"4#3#3T]]DOOTDNNN! 	@ /"4==1!
r-   c                J    d| _         d| _        d| _        d| _        d| _        y Nr   )nb_valid
nb_ignorednb_warningsnb_erroneousnb_totalr_   s    r+   rY   zMigrationLinter.reset_countersn   s'    r-   c                J    t        | j                  xr | j                         S r(   )boolrD   rN   ri   s    r+   rZ   z MigrationLinter.should_use_cacheu   s    D$$:T]]):;;r-   c                   | j                  |      }|r| j                  ||      }n| j                  |      }t        |d       }|r|r| j                  j                  ||      nd }|D ]S  }	|r|r|	|k(  s| j                  |	       |r"|	j                  |k(  s1| j                  |	       C| j                  |	       U | j                         r| j                  j                          y y )Nc                2    | j                   | j                  fS r(   )	app_labelname)	migrations    r+   r,   z5MigrationLinter.lint_all_migrations.<locals>.<lambda>   s    y/B/BINN.S r-   )key)read_migrations_list_gather_migrations_git_gather_all_migrationssortedr^   get_migration_by_prefixlint_migrationrn   rZ   r\   save)
r_   rn   migration_namegit_commit_idmigrations_file_pathmigrations_list
migrationssorted_migrationsspecific_target_migrationms
             r+   lint_all_migrationsz#MigrationLinter.lint_all_migrationsx   s     334HI44]OTJ44_EJ #S
 ^ !!99)^T 	" # 	'A^11''*;;)+''*##A&	'   "NN! #r-   c                   |j                   }|j                  }|j                  }| xj                  dz  c_        | j	                  ||      }| j                  ||||j                        r8| j                  ||dt        j                         | xj                  dz  c_
        y | j                         r"|| j                  v r| j                  |||       y | j                  ||      }t        | j                   || j"                        \  }}}	| j%                  |      \  }
}}|
r||
z  }|r||z  }|r|	|z  }	| j&                  r||	z  }g }	nR| j(                  rFg }|	D ]=  }|j*                  | j(                  v r|j-                  |       -|j-                  |       ? |}	|rb| j                  ||dt        j.                         | xj0                  dz  c_        | j3                  |       |	r| j5                  |	       d||	d}n|	rN| j                  ||dt        j6                         | xj8                  dz  c_        | j5                  |	       d|	d}nq|r4| j                  ||dt        j                         | j3                  |       n"| j                  ||d	t        j:                         | xj<                  dz  c_        d
d	i}| j                         r|| j>                  |<   y y )Nr   )
is_initialr8   ERR)resulterrorswarningsr9   )r   r   zOK (ignored)r7   r   ) rn   ro   
operationsrh   get_migration_hashshould_ignore_migrationinitialprint_linting_msgr!   r8   re   rZ   r[   lint_cached_migrationget_sqlr   rV   rK   analyse_data_migrationrS   rR   codeappendr:   rg   print_errorsprint_warningsr9   rf   r7   rd   r\   )r_   rp   rn   ry   r   md5hashsql_statementsr   ignoredr   errignored_datawarnings_datanew_warningswvalue_to_caches                   r+   rw   zMigrationLinter.lint_migration   s   ''	"))
)))^D''~zi>O>O ( 
 "">8[5G5G OOq O  "w$..'@&&y.'Ji@$:##((%
! ,0+F+Fy+Q(\=cMF|#G%H&&hFH**L +66T:::MM!$ ''*	+
 $H ""9ne[EVEVW"f%##H-(-XVN"">9k6I6I !)(1xHN &&~~{?Q?Q !!'*&&y.$WMMQM&-N  "&4DNN7# #r-   c                   t        j                  d      }t        t        | |      d      5 t	        fdd      D ]  }|j                  |        	 d d d        |j                         S # 1 sw Y   |j                         S xY w)NF)usedforsecurityrbc                 &     j                  d      S )Ni   )read)fs   r+   r,   z4MigrationLinter.get_migration_hash.<locals>.<lambda>   s    affTl r-   r-   )hashlibmd5openr   iterupdate	hexdigest)rn   ry   hash_md5chunkr   s       @r+   r   z"MigrationLinter.get_migration_hash   sz    ;;u5'	>BDI 	'Q2C8 '&'	' !!##	' !!##s   &A--Bc                   | j                   |   }|d   dk(  r9| j                  ||dt        j                         | xj                  dz  c_        n |d   dk(  r8| j                  ||dt        j
                         | xj                  dz  c_        n|d   dk(  rL| j                  ||dt        j                         | xj                  dz  c_        | j                  |d	          nl| j                  ||d
t        j                         | xj                  dz  c_        d|v r| j                  |d          d	|v r|d	   r| j                  |d	          || j                  |<   y )Nr   r8   zIGNORE (cached)r   r7   zOK (cached)r9   zWARNING (cached)r   zERR (cached)r   )r[   r   r!   r8   re   r7   rd   r9   rf   r   r:   rg   r   r\   )r_   rn   ry   r   cached_values        r+   r   z%MigrationLinter.lint_cached_migration   sQ    ~~g.!X-"">+<k>P>P OOq O(#t+"">=+.. MMQM(#y0"">+={?R?R !Z 89"">>;;L;L "<'!!,x"89\)l:.F##L$<=".wr-   c                x    |j                   | j                  v ry | j                  st        d| d| d|        y y )N(z, z)... )r)   rQ   rT   print)r_   rn   ry   msglint_results        r+   r   z!MigrationLinter.print_linting_msg  sA     

*~~Ai[>"2%u=> r-   c                x   t         j                  j                  | j                  v ry |D ]  }dj	                  |j
                        }|j                  rM|dj	                  |j                        z  }|j                  r|dj	                  |j                        z  }|dz  }| j                  rt        |        y )N	{}z (table: {}z, column: {}))
r!   r:   r)   rQ   formatmessagetablecolumnrT   r   )r_   r   r   	error_strs       r+   r   zMigrationLinter.print_errors  s    ""djj0 	!Cckk2Iyy]11#))<<	::!6!6szz!BBIS 	>>i 	!r-   c                    t         j                  j                  | j                  v ry |D ]5  }dj	                  |j
                        }| j                  r+t        |       7 y )Nr   )r!   r9   r)   rQ   r   r   rT   r   )r_   r   warning_detailswarn_strs       r+   r   zMigrationLinter.print_warnings)  sN    $$

2' 	 O}}_%<%<=H>>h	 r-   c                \   | j                   ry t        d       t        d| j                   d| j                          t        d| j                   d| j                          t        d| j
                   d| j                          t        d| j                   d| j                          y )Nz*** Summary ***zValid migrations: /zErroneous migrations: zMigrations with warnings: zIgnored migrations: )rT   r   rd   rh   rg   rf   re   ri   s    r+   print_summaryzMigrationLinter.print_summary2  s    >> "4==/4==/BC&t'8'8&94==/JK*4+;+;*<Admm_MN$T__$5Qt}}oFGr-   c                     | j                   dkD  S rc   )rg   ri   s    r+   
has_errorszMigrationLinter.has_errors;  s      1$$r-   c           	        t         j                  d| d|        	 t        t        j                  d      5 }t        d||| j                  |      }d d d        j                         S # 1 sw Y   xY w# t        t        f$ rk}| j                  r$t         j                  d||t        |             d}n"t         j                  d||t        |              Y d }~|j                         S d }~ww xY w)	NzCalling sqlmigrate command  r   
sqlmigrate)rL   stdoutzeError while executing sqlmigrate on (%s, %s) with exception: %s. Continuing execution with empty SQL. z@Error while executing sqlmigrate on (%s, %s) with exception: %s.)loggerinfor   osdevnullr   rL   
ValueErrorr   rW   r$   str
splitlines)r_   rn   ry   dev_nullsql_statementr   s         r+   r   zMigrationLinter.get_sql?  s    1)An=MNO	bjj#& ( , "!]]#!4 ''))5  ,- 	,,;"H !#V"H	   ''))%	s/   A6 A*A6 *A3/A6 6C0AC++C0c                `    ddl m} t        t        j                  d| d|       xr d| v      S )Nr   )MIGRATIONS_MODULE_NAMEr   z/.*\.pyra   )r]   r   rk   research)filenamer   s     r+   is_migration_filez!MigrationLinter.is_migration_file^  s8    FII12':HE +(*
 	
r-   c                T   |syg }	 t        |      5 }|D ]5  }| j                  |      st        |      \  }}|j                  ||f       7 	 ddd       |st
        j                  d|       |S # 1 sw Y   #xY w# t        $ r" t
        j                  d|       t        d      w xY w)z
        Returning an empty list is different from returning None here.
        None: no file was specified and we should consider all migrations
        Empty list: no migration found in the file and we should consider no migration
        Nz!Migrations list path not found %sz(Error while reading migrations list filez8No valid migration paths found in the migrations file %s)	r   r   r   r   OSErrorr   	exception	Exceptionr   )clsr{   r}   filelinern   ro   s          r+   rr   z$MigrationLinter.read_migrations_listg  s     $
	H*+ =t  =D,,T2*>t*D	4"))9d*;<== KKJ$ = =
  	H@BVWFGG	Hs'   A< A0#A0A< 0A95A< <+B'c                J   g }ddddd|g}t         j                  d| d| j                   d       t        |t        t        | j                  	      }t        t        |j                  j                               D ]  }| j                  |      st        |      \  }}|||f|v s,||f| j                  j                  v r-| j                  j                  ||f   }	|j                  |	       st         j                  d
||        |j                          |j                  dk7  r~g }
t        t        |j                   j                               D ]  }|
j                  |        t         j#                  dj%                  dj'                  |
                   t)        d      |S )Ngitdiffz
--relativez--name-onlyz--diff-filter=ARz
Executing z (in r   )r   stderrcwdzFFound migration file (%s, %s) that is not present in loaded migration.r   z Error while git diff command:
{}r   z&Error while executing git diff command)r   r   rD   r   r   r/   r   r   	readlinesr   r   r^   disk_migrationsr   wait
returncoder   r%   r   joinr   )r_   rz   r|   r}   git_diff_commanddiff_processr   rn   ro   rp   outputs              r+   rs   z&MigrationLinter._gather_migrations_git  s    
 
 	j!1 2%8H8H7IKL  	
  3 3 = = ?
 	D %%d+"6t"<	4"*y$.??.R!4(D,A,A,Q,QQ$($9$9$I$I%tO%	 #)))4G% 		& 	""a'F"L$7$7$A$A$C $ d#$ LL<CCBGGFOTUDEEr-   c              #     K   | j                   j                  j                         D ]  \  \  }}}|t        vs|||f|v s|   y wr(   )r^   r   itemsr   )r_   r|   rn   ro   rp   s        r+   rt   z&MigrationLinter._gather_all_migrations  sY      ""2288:	$ 
Y ;;"*y$.??.R#O	$s   6A	A	A	c                @   | j                   xr || j                   vxs  | j                  xr || j                  v xs t        d |D              xs | j                  xr | j                  |v xs | j                  xr | j                  |vxs || j
                  v xs | j                  xr || j                  vxsd | j                  xr ||f| j                  j                  vxs: | j                  xr ||f| j                  j                  v xs | j                  xr |S )Nc              3  <   K   | ]  }t        |t                y wr(   )
isinstancer   ).0os     r+   	<genexpr>z:MigrationLinter.should_ignore_migration.<locals>.<genexpr>  s     Fa:a1Fs   )rI   rJ   anyrE   rG   rF   rH   rO   r^   applied_migrationsrP   rX   )r_   rn   ry   r   r   s        r+   r   z'MigrationLinter.should_ignore_migration  sI    E9D4E4E#E ?!!Di43D3D&D?F:FF? )) @--?? ** E..nD? $"2"22? !!MnD<M<M&M? ,, @/,,??@?& .. </((;;<)?. ..=:1	
r-   c                
   g }g }g }|j                   D ]j  }t        |t              r| j                  |      \  }}}n,t        |t              r| j                  |      \  }}}ng g g }}}|r||z  }|r||z  }|sf||z  }l |||fS r(   )r   r   r   lint_runpythonr   lint_runsql)	r_   rp   r   r   r   	operation	op_errors
op_ignoredop_warningss	            r+   r   z&MigrationLinter.analyse_data_migration  s     "-- 	(I)Y/595H5H5S2	:{Iv.595E5Ei5P2	:{57R{:	)#:%K'	( w((r-   c                R    t        | t        j                        r| j                  S | S r(   )r   	functoolspartialfunc)functions    r+   discover_functionz!MigrationLinter.discover_function  s!    h	 1 12== r-   c                p   | j                  |j                        j                  }g }g }g }|j                  sWt	        ddj                  |            }|j                  | j                  v r|j                  |       n|j                  |       t        j                  |j                        }t        |j                        t        k7  rWt	        ddj                  |            }|j                  | j                  v r|j                  |       n|j                  |       | j                  |j                        }|D ]=  }|j                  | j                  v r|j                  |       -|j                  |       ? |j                  r_| j                  |j                        }|D ]?  }|r*|j                  | j                  v r|j                  |       /|j                  |       A | j                  |j                        }|D ]?  }|r*|j                  | j                  v r|j                  |       /|j                  |       A |j                  r_| j                  |j                        }|D ]?  }|r*|j                  | j                  v r|j                  |       /|j                  |       A |||fS )NRUNPYTHON_REVERSIBLEz0'{}': RunPython data migration is not reversibler   r    RUNPYTHON_ARGS_NAMING_CONVENTIONzK'{}': By convention, RunPython names the two arguments: apps, schema_editor)r   r   r4   
reversibler   r   rK   r   inspectgetfullargspectupleargsr   !get_runpython_model_import_issuesreverse_code*get_runpython_model_variable_naming_issues)	r_   	runpythonfunction_namer%   r   r$   issue	args_specissuess	            r+   r   zMigrationLinter.lint_runpython  s_    ..y~~>GG ##+JQQ!E zzT999u%u% **9>>:	 $@@7M&'E zzT999u%u% 77	G 	$EzzT999u%U#		$ ;;I<R<RSF (UZZ4+G+GGNN5)LL'	( @@P 	&Et'C'CCu%u%		& DD&&F   *UZZ4+G+GGNN5)NN5)	* gw&&r-   c           
        t        j                  d      }t        j                  |       }|j                  }t        j                  |      }|j                  |      }g }|D ]a  }|j                  dd      d   }t        j                  | d|      d u}|r6|j                  t        ddj                  ||                   c |S )	Nz)[^a-zA-Z0-9._]?([a-zA-Z0-9._]+?)\.objects.r   r   z.*= +\w+\.get_model\(RUNPYTHON_MODEL_IMPORTzz'{}': Could not find an 'apps.get_model("...", "{}")' call. Importing the model directly is incorrect for data migrations.r  )r   compiler=   r   r4   r  	getsourcefindallsplitr   r   r   r   )	r   model_object_regexr   r  source_codecalled_modelsr  modelhas_get_model_calls	            r+   r
  z1MigrationLinter.get_runpython_model_import_issuesM  s    ZZ(TU"44T: ))''1*22;?" 	EKKQ'*E		w34 	  &5/ !&6		( r-   c           
     R   t        j                  d      }t        j                  |       }|j                  }t        j                  |      }|j                  |      }g }|D ]  }t        j                  dj                  |      |t         j                  t         j                  z        d uxsH t        j                  dj                  |      |t         j                  t         j                  z        d u}|r|j                  t        ddj                  ||                    |S )Nz"[^a-zA-Z]?([a-zA-Z0-9]+?)\.objectsz1{model}.*= +\w+?\.get_model\([^)]+?\.{model}.*?\))r  z6{model}.*= +\w+?\.get_model\([^)]+?,[^)]*?{model}.*?\)RUNPYTHON_MODEL_VARIABLE_NAMEzs'{}': Model variable name {} is different from the model class name that was found in the apps.get_model(...) call.r  )r   r  r=   r   r4   r  r  r  r   r   	MULTILINEDOTALLr   r   )	r   r  r   r  r  r  r  r  has_same_model_names	            r+   r  z:MigrationLinter.get_runpython_model_variable_naming_issuesm  s'   ZZ(MN"44T: ))''1*22;?" 	E		HOO# P   LL299,   99MTT# U   LL299,   $ '<8 !&6	)	< r-   c                   g }g }g }|j                   sHt        dd      }|j                  | j                  v r|j	                  |       n|j	                  |       |j
                  t        j                  k7  rg }t        |j
                  t        t        f      rt|j
                  D ]d  }d }t        |t        t        f      r9t        |      }	|	dk(  r|\  }}nt        d|	z        |j	                  ||z         T|j	                  |       f n|j	                  |j
                         t        | j                  || j                        \  }
}}|
r||
z  }|r||z  }|r||z  }|j                   r|j                  t        j                  k7  rg }t        |j                  t        t        f      rt|j                  D ]d  }d }t        |t        t        f      r9t        |      }	|	dk(  r|\  }}nt        d|	z        |j	                  ||z         T|j	                  |       f n|j	                  |j                         t        | j                  || j                        \  }
}}|
r||
z  }|r||z  }|r||z  }|||fS )NRUNSQL_REVERSIBLEz'RunSQL data migration is not reversibler     zExpected a 2-tuple but got %d)r  r   r   rK   r   sqlr   noopr   r.   r  lenr   r   rV   reverse_sql)r_   runsqlr%   r   r$   r  r   r&  paramselements
sql_errorssql_ignoredsql_warningss                r+   r   zMigrationLinter.lint_runsql  so       (AE zzT999u%u% ::$N&**tUm4!:: 
3C!F!#e}5#&s8#q=*-KC",-Lx-W"XX&--cFl;&--c2
3 %%fjj14J'',,51J\
 #;&<' !3!3v{{!BN&,,tUm<!-- 
3C!F!#e}5#&s8#q=*-KC",-Lx-W"XX&--cFl;&--c2
3 %%f&8&894J'',,51J\
 #;&<'gw&&r-   )(r`   
str | NonerE   r0  rF   Iterable[str] | NonerG   r0  rH   r1  rI   r1  rJ   r1  rL   r   rM   r   rN   rk   rO   rk   rP   rk   rK   r1  rQ   r1  rR   r1  rS   rk   rT   rk   r@   r0  rW   rk   rX   rk   )r2   None)r2   rk   )NNNN)
rn   r0  ry   r0  rz   r0  r{   r0  r2   r2  )rp   r   r2   r2  )rn   r   ry   r   r2   r   )rn   r   ry   r   r   r   r2   r2  )
rn   r   ry   r   r   r   r   r!   r2   r2  )r   list[Issue]r2   r2  )r   r3  r2   r2  )rn   r   ry   r   r2   r3   )r   r   r2   rk   )r{   r0  r2   list[tuple[str, str]] | Noner(   )rz   r   r|   r4  r2   Iterable[Migration])r|   r4  r2   r5  )r0   F)
rn   r   ry   r   r   zIterable[Operation]r   rk   r2   rk   )rp   r   r2   ,tuple[list[Issue], list[Issue], list[Issue]])r  r   r2   r6  )r   r   r2   r3  )r*  r   r2   r6  ) r4   r5   r6   r   r   ra   rY   rZ   r   rw   r;   r   r   r   r   r   r   propertyr   r   r   classmethodrr   rs   rt   r   r   r   r   r
  r  r   r0   r-   r+   r=   r=   -   s*     +/,0,0-1-1-1(,(-*/8<&*9=',&*).*/+>
>
 )>
 *	>

  *>
 +>
 +>
 +>
 >
 >
 >
 "&>
 $(>
 "6>
 $>
  #7!>
" !%#>
$ %>
& $'>
( #')>
* $(+>
@<
 !%%)$(+/$"$" #$" "	$"
 )$" 
$"LL5\ $ $//.1/<?/	/@??.1?8;?JU?	?! H % %*> 
 
 #-	% < SW1 13O1	1h ?C	$;	$		$ +-  
 
  
 (	 

  
 
 
D)")	5)0  
H'"H'	5H'T  > ' 'RN'N'	5N'r-   r=   )4
__future__r   r   r   r  loggingr   r   enumr   r   
subprocessr   r   typingr   r	   django.confr
   django.core.managementr   	django.dbr   r   r   django.db.migrationsr   r   r   $django.db.migrations.operations.baser   cacher   	constantsr   r   r   r   r   sql_analyserr   r   sql_analyser.baser   utilsr   r   r   	getLoggerr   r!   r=   r0   r-   r+   <module>rI     s    "     	 	  " %   / E E = = :  
 ( H $ R R			4	5 9$ 9 9x
' x
'r-   