U of@sddlmZddlmZddlmZddlZddlZddlmZddlm Z ddlm Z ddlm Z dd lm Z dd lm Z dd lmZdd lmZdd lmZddlmZddlmZddlmZddlmZddlmZddlmZddlmZddlmZddlmZddlmZddlmZddlmZddlmZ ddl!m"Z"ddl#m$Z$ddl#m%Z%ddl%m&Z&dd l'm(Z(erdd!lm)Z)dd"lm*Z*dd#l+m,Z,dd$l+m-Z-dd%l.m/Z/dd&l0m1Z1d'd(l2m3Z3dd)l4m5Z5dd*l6m7Z7dd+l6m8Z8dd,l9m:Z:dd-l9m;Z;dd.l9mZ?Gd/d0d0Z@Gd1d2d2ZAGd3d4d4ZBGd5d6d6ZCGd7d8d8ZDGd9d:d:eDZEGd;d<dCs z_ProxyTransaction.rollbackcCs&|j}|dk st|d|j_dSr4)r<r=commitr2r:r?r6r6r7rAIs z_ProxyTransaction.commitcCs|Sr4r6r;r6r6r7 __enter__Osz_ProxyTransaction.__enter__r)type_value tracebackr3cCs&|jdk r"|j|||d|j_dSr4)r<__exit__r2r:)r5rCrDrEr6r6r7rFRs z_ProxyTransaction.__exit__N) __name__ __module__ __qualname__r8propertyr<r>rArBrFr6r6r6r7r/;sr/c @sJeZdZdZdHddddddd d ZedIdd d d dddddddZeddddZdJdddddZ d dddZ ddd d!Z dKddd"d#d$Z ddd%d&Z d'd(dd)d*d+Zd,dd-d.d/Zddd0d1ZdLd2d3dd4d5d6Zdd7d8d9d:Zeddd;d<Zed=dd>d?Zd@dAddBdCdDZd@d@d d ddEdFdGZdS)Mr0aRepresent the database state made available to a migration script. :class:`.MigrationContext` is the front end to an actual database connection, or alternatively a string output stream given a particular database dialect, from an Alembic perspective. When inside the ``env.py`` script, the :class:`.MigrationContext` is available via the :meth:`.EnvironmentContext.get_context` method, which is available at ``alembic.context``:: # from within env.py script from alembic import context migration_context = context.get_context() For usage outside of an ``env.py`` script, such as for utility routines that want to check the current version in the database, the :meth:`.MigrationContext.configure` method to create new :class:`.MigrationContext` objects. For example, to get at the current revision in the database using :meth:`.MigrationContext.get_current_revision`:: # in any application, outside of an env.py script from alembic.migration import MigrationContext from sqlalchemy import create_engine engine = create_engine("postgresql://mydatabase") conn = engine.connect() context = MigrationContext.configure(conn) current_rev = context.get_current_revision() The above context can also be used to produce Alembic migration operations with an :class:`.Operations` instance:: # in any application, outside of the normal Alembic environment from alembic.operations import Operations op = Operations(context) op.alter_column("mytable", "somecolumn", nullable=True) Nr!zOptional[Connection]zDict[str, Any]zOptional[EnvironmentContext]r1)dialect connectionoptsenvironment_contextr3c Cs||_||_||_|d|_|dd}|d}|dd|_|dd|_d|_|rtt d| ||_ |j dk s~t d|_ n||_ t||_ |d |_||_|d d|_d |krt|d ptj|d |_n|d tj|_|d d|_|dd|_|dd|_}|dd|_}t|ttdtddd|d|_|ddrv|j t!dd|d|d|_"t#j$%|||j |j||j||_&t'(d|j&j)j*|jrt'(dt'(d|j&j+rdnddS) Nscriptas_sqlFtransactional_ddlZtransaction_per_migrationZon_version_applyr6r#fnpurgeZoutput_encoding output_buffer compare_typeTcompare_server_default version_tableZalembic_versionversion_table_schema version_num )Znullable)ZschemaZversion_table_pkz%s_pkc)nameZ starting_revzContext impl %s.zGenerating static SQLzWill assume %s DDL.Z transactionalznon-transactional),rNrMrKgetrO_transaction_per_migrationon_version_apply_callbacksr:rr_stdout_connectionrLr=_in_external_transactionrZ_get_connection_in_transaction_migrations_fnrPrSr sysstdoutrT_user_compare_type_user_compare_server_defaultrWrXrrrr_versionZappend_constraintr_start_from_revrZ DefaultImplZget_by_dialectimplloginfo __class__rGrQ) r5rKrLrMrNrPrQrWrXr6r6r7r8s         zMigrationContext.__init__zOptional[Union[str, URL]] Optional[str]zOptional[Dialect]zOptional[Dict[str, str]]z Optional[Any])rLr dialect_namerKrN dialect_optsrMr3c Cs|dkr i}|dkri}|r.begin_commit) r`rrhrQr]r=rPr:rLrZ"_safe_begin_connection_transactionr/r)r5r}Ztransaction_nowin_transactionr~r6r;r7begin_transactionys:,   z"MigrationContext.begin_transactioncCsB|}t|dkrdSt|dkr6td|jn|dSdS)aReturn the current revision, usually that which is present in the ``alembic_version`` table in the database. This method intends to be used only for a migration stream that does not contain unmerged branches in the target database; if there are multiple branches present, an exception is raised. The :meth:`.MigrationContext.get_current_heads` should be preferred over this method going forward in order to be compatible with branch migration support. If this :class:`.MigrationContext` was configured in "offline" mode, that is with ``as_sql=True``, the ``starting_rev`` parameter is returned instead, if any. rNr'zQVersion table '%s' has more than one head present; please use get_current_heads())get_current_headslenrrprWr5headsr6r6r7get_current_revisions  z%MigrationContext.get_current_revisionTuple[str, ...]csjrNj}|dkrd}n&|dk r@jr@fddt|D}tj|ddSjr^tdsjdSjdk sxt t dd j j DS) aReturn a tuple of the current 'head versions' that are represented in the target database. For a migration stream without branches, this will be a single value, synonymous with that of :meth:`.MigrationContext.get_current_revision`. However when multiple unmerged branches exist within the target database, the returned tuple will contain a value for each head. If this :class:`.MigrationContext` was configured in "offline" mode, that is with ``as_sql=True``, the ``starting_rev`` parameter is returned in a one-length tuple. If no version table is present, or if there are no revisions present, an empty tuple is returned. baseNcs"g|]}|dkrj|jqS))Nr)rO get_revisionrevision).0Zsfrr;r6r7 sz6MigrationContext.get_current_heads..r6defaultzECan't specify current_rev to context when using a database connectioncss|]}|dVqdS)rNr6)rrowr6r6r7 sz5MigrationContext.get_current_heads..)rPrgrOrZto_listto_tuplerp_has_version_tablerLr=tupleexecuterfselect)r5Zstart_from_revr6r;r7rs& z"MigrationContext.get_current_heads)rSr3c Cs`t|jJ|jdk st|jj|jdd|rR|jdk s@t|j|jW5QRXdS)NT)Z checkfirst)rZ_ensure_scope_for_ddlrLr=rfcreaterdelete)r5rSr6r6r7_ensure_version_table"s z&MigrationContext._ensure_version_tablecCs"|jdk stt|j|j|jSr4)rLr=rZ_connectable_has_tablerWrXr;r6r6r7r*s z#MigrationContext._has_version_tabler+str)script_directoryrr3cCsD|}|js|s|t||}|||D]}||q0dS)a\Stamp the version table with a specific revision. This method calculates those branches to which the given revision can apply, and updates those branches as though they were migrated towards that revision (either up or down). If no current branches include the revision, it is added as a new branch head. N)rrPrHeadMaintainerZ _stamp_revsupdate_to_step)r5rrrhead_maintainerstepr6r6r7stamp0s   zMigrationContext.stamprkwr3c KsZ|j|jr2|jr td|jddd}n,|}|j dd}|js^|s^|s^|t ||}|j dk svt | ||D]}|j dd|jr|js|jdk st |j|jtd ||jr|jd |jf|jf||||jD]}|||jt|j|d qW5QRXq|jrV|jsV|jdk sHt |j|jdS) a-Run the migration scripts established for this :class:`.MigrationContext`, if any. The commands in :mod:`alembic.command` will set up a function that is ultimately passed to the :class:`.MigrationContext` as the ``fn`` argument. This function represents the "work" that will be done when :meth:`.MigrationContext.run_migrations` is called, typically from within the ``env.py`` script of the migration environment. The "work function" then provides an iterable of version callables and other version information which in the case of the ``upgrade`` or ``downgrade`` commands are the list of version scripts to invoke. Other commands yield nothing, in the case that a command wants to run some other operation against the database such as the ``current`` or ``stamp`` commands. :param \**kw: keyword arguments here will be passed to each migration callable, that is the ``upgrade()`` or ``downgrade()`` method within revision scripts. z!Can't use --purge with --sql modeT)rSr6 dont_mutateFN)r}z Running %sz -- Running %s)ctxrrZrun_args)rhZstart_migrationsrSrPrrprrrMr\rrar=rrrLrfrrirjZ static_output short_log migration_fnrr^setZdrop)r5rrrrrcallbackr6r6r7run_migrations@sD          zMigrationContext.run_migrationscCs.z |jj}Wntk r"YdSX|SdSNF)rLrAttributeError)r5methr6r6r7rvs  z+MigrationContext._in_connection_transactionzUnion[Executable, str]zOptional[Dict[str, Any]])sqlrxr3cCs|j||dS)aExecute a SQL construct or string statement. The underlying execution mechanics are used, that is if this is "offline mode" the SQL is written to the output buffer, otherwise the SQL is emitted on the current SQLAlchemy connection. Nrh_exec)r5rrxr6r6r7rs zMigrationContext.executer%)rLr3csfdd}tj|S)Ncsj|dSr4r)Z constructZ multiparamsparamsr;r6r7dumpsz1MigrationContext._stdout_connection..dump)rr%rK)r5rLrr6r;r7r_s z#MigrationContext._stdout_connectioncCs|jS)aReturn the current "bind". In online mode, this is an instance of :class:`sqlalchemy.engine.Connection`, and is suitable for ad-hoc execution of any kind of usage described in SQLAlchemy Core documentation as well as for usage with the :meth:`sqlalchemy.schema.Table.create` and :meth:`sqlalchemy.schema.MetaData.create_all` methods of :class:`~sqlalchemy.schema.Table`, :class:`~sqlalchemy.schema.MetaData`. Note that when "standard output" mode is enabled, this bind will be a "mock" connection handler that cannot return results and is only appropriate for a very limited subset of commands. )rLr;r6r6r7bindszMigrationContext.bindzOptional[Config]cCs|jr|jjSdSdS)zLReturn the :class:`.Config` used by the current environment, if any.N)rNconfigr;r6r6r7rszMigrationContext.configz Column[Any]r)inspector_columnmetadata_columnr3cCsH|jdkrdSt|jr:|||||j|j}|dk r:|S|j||Sr)rdcallabletyperhrU)r5rr user_valuer6r6r7 _compare_types  zMigrationContext._compare_type)rrrendered_metadata_defaultrendered_column_defaultr3cCsL|jdkrdSt|jr:||||||j|}|dk r:|S|j||||Sr)rerZserver_defaultrhrV)r5rrrrrr6r6r7_compare_server_defaults&  z(MigrationContext._compare_server_default)N)NNNNNNN)F)F)N)rGrHrI__doc__r8 classmethodrurr{rrrrrrrrvrr_rJrrrrr6r6r6r7r0Xs@4[ 7^d*H  r0c@s`eZdZddddddZdddd d Zdddd d Zdddd ddZdddddZdS)rr0rr1)contextrr3cCs||_t||_dSr4)rrr)r5rrr6r6r7r8szHeadMaintainer.__init__r)versionr3cCsD||jkst|j||jj|jjjt d|ddS)N'%s'rY) rr=addrrhrrfinsertvaluesr)r5rr6r6r7_insert_versions   zHeadMaintainer._insert_versioncCs~|j||jj|jj|jjjj t d|k}|jj sz|jj j rz|dk rz|jdkrztd||jj|jfdS)Nrr'zOOnline migration expected to match one row when deleting '%s' in '%s'; %d found)rremoverrhrrfrwherecrYrrPrKsupports_sane_rowcountrowcountrrprW)r5rretr6r6r7_delete_versions*    zHeadMaintainer._delete_version)from_to_r3cCs||jkst|j||j||jj|jjj t d|d |jjj j t d|k}|jjs|jjjr|dk r|jdkrtd|||jj|jfdS)Nrrr'zWOnline migration expected to match one row when updating '%s' to '%s' in '%s'; %d found)rr=rrrrhrrfupdaterrrrrYrPrKrrrrprW)r5rrrr6r6r7_update_versions2       zHeadMaintainer._update_versionzUnion[RevisionStep, StampStep])rr3c Cs"||jr*|j}td|||n||jrT|j}td|||n| |jr| |j\}}}td||||D]}||q| ||nz| |jr| |j\}}}td||||D]}||q| ||n*||j\} } td| | | | | dS)Nzbranch delete %sznew branch insert %sz!merge, delete %s, update %s to %sz#unmerge, insert %s, update %s to %szupdate %s to %s)should_delete_branchrdelete_version_numridebugrshould_create_branchinsert_version_numrshould_merge_branchesmerge_branch_identsrshould_unmerge_branchesunmerge_branch_identsupdate_version_num) r5rversZ delete_revsZupdate_from_revZ update_to_revZdelrevZ insert_revsZinsrevrrr6r6r7r8sR            zHeadMaintainer.update_to_stepN)rGrHrIr8rrrrr6r6r6r7rs  rc@seZdZUdZded<ded<ded<ded<ded <d ed <d ddd d d dddZeddddZeddddZeddddZ eddddZ eddddZ eddddZ eddd d!Z eddd"d#Zd$S)% MigrationInfozExposes information about a migration step to a callback listener. The :class:`.MigrationInfo` object is available exclusively for the benefit of the :paramref:`.EnvironmentContext.on_version_apply` callback hook. r| is_upgradeis_stamprlup_revision_idrup_revision_idsdown_revision_idsr. revision_mapzUnion[str, Tuple[str, ...]]r1)rrr up_revisionsdown_revisionsr3cCsP||_||_||_tj|dd|_|jr6|jd|_nd|_tj|dd|_dS)Nr6rr)rrrrrrrr)r5rrrrrr6r6r7r8szMigrationInfo.__init__r9cCs|j S)aTrue/False: indicates whether this operation is a migration. At present this is true if and only the migration is not a stamp. If other operation types are added in the future, both this attribute and :attr:`~.MigrationInfo.is_stamp` will be false. )rr;r6r6r7 is_migrationszMigrationInfo.is_migrationcCs|jr |jS|jS)z7Active revisions before this migration step is applied.)rrrr;r6r6r7source_revision_idssz!MigrationInfo.source_revision_idscCs|jr |jS|jS)z6Active revisions after this migration step is applied.)rrrr;r6r6r7destination_revision_idssz&MigrationInfo.destination_revision_idszOptional[Revision]cCs|j|jS)zUGet :attr:`~.MigrationInfo.up_revision_id` as a :class:`.Revision`. )rrrr;r6r6r7 up_revisionszMigrationInfo.up_revisionz%Tuple[Optional[_RevisionOrBase], ...]cCs|j|jS)zLGet :attr:`~.MigrationInfo.up_revision_ids` as a :class:`.Revision`.)r get_revisionsrr;r6r6r7rszMigrationInfo.up_revisionscCs|j|jS)zcGet :attr:`~.MigrationInfo.down_revision_ids` as a tuple of :class:`Revisions <.Revision>`.)rrrr;r6r6r7rszMigrationInfo.down_revisionscCs|j|jS)zdGet :attr:`~MigrationInfo.source_revision_ids` as a tuple of :class:`Revisions <.Revision>`.)rrrr;r6r6r7source_revisionsszMigrationInfo.source_revisionscCs|j|jS)ziGet :attr:`~MigrationInfo.destination_revision_ids` as a tuple of :class:`Revisions <.Revision>`.)rrrr;r6r6r7destination_revisionssz#MigrationInfo.destination_revisionsN)rGrHrIr__annotations__r8rJrrrrrrrrr6r6r6r7rfs0   rc@seZdZUded<ded<ded<ded<er@edd d d Zed d d dZeddddddZ eddddddZ edd ddZ ed d ddZ ddZ dS) MigrationSteprfrom_revisions_no_depsto_revisions_no_depsr|rrrrlr9cCsdSr4r6r;r6r6r7docszMigrationStep.docrcCs|jjSr4)rrGr;r6r6r7r[szMigrationStep.namer.r* RevisionStep)rrOr3cCs t||dS)NTrrtrrOr6r6r7upgrade_from_scriptsz!MigrationStep.upgrade_from_scriptcCs t||dSrrrr6r6r7downgrade_from_scriptsz#MigrationStep.downgrade_from_scriptcCs|j Sr4)rr;r6r6r7 is_downgradeszMigrationStep.is_downgradecCs d|jt|jt|jfS)Nz %s %s -> %s)r[rformat_as_commarrr;r6r6r7rs   zMigrationStep.short_logcCs4|jr*d|jt|jt|j|jfS|jSdS)Nz%s %s -> %s, %s)rr[rrrrrr;r6r6r7__str__s  zMigrationStep.__str__N)rGrHrIrrrJrr[rrrrrrr6r6r6r7rs$ rc@sZeZdZdddddddZdd Zd dd d d ZeddddZeddddZeddddZ eddddZ eddddZ eddddZ ddddd Z dd!dd"d#Zdddd$d%Zdd&dd'd(Zdddd)d*Zdddd+d,Zdddd-d.Zdd/dd0d1Zed2dd3d4Zed2dd5d6Zed7dd8d9Zd:S);rr.r*r|r1)rrrr3cCs0||_||_||_|r"|jj|_n |jj|_dSr4)rrrmoduleupgrader downgrade)r5rrrr6r6r7r8s  zRevisionStep.__init__cCsd|jj|jfS)NzRevisionStep(%r, is_upgrade=%r))rrr;r6r6r7__repr__(szRevisionStep.__repr__object)otherr3cCs"t|to |j|jko |j|jkSr4)rorrrr5rr6r6r7__eq__.s    zRevisionStep.__eq__rlr9cCs|jjSr4)rrr;r6r6r7r5szRevisionStep.docrcCs|jr|jjS|jjfSdSr4rr_normalized_down_revisionsr;r6r6r7from_revisions9szRevisionStep.from_revisionscCs|jr|jjS|jjfSdSr4rrZ_versioned_down_revisionsr;r6r6r7r@sz#RevisionStep.from_revisions_no_depscCs|jr|jjfS|jjSdSr4rr;r6r6r7 to_revisionsIs zRevisionStep.to_revisionscCs|jr|jjfS|jjSdSr4rr;r6r6r7rPs z!RevisionStep.to_revisions_no_depscCst|jjdkSNr')rrrr;r6r6r7_has_scalar_down_revisionYsz&RevisionStep._has_scalar_down_revisionSet[str]rr3cCs>|js dS|jj|krdS|jj}|s*dS||}| SdS)zA delete is when we are a. in a downgrade and b. we are going to the "base" or we are going to a version that is implied as a dependency on another version that is remaining. FTN)rrr_unmerge_to_revisions)r5rdownrevsrr6r6r7r]s  z!RevisionStep.should_delete_branchzTuple[List[str], str, str]cCsvt||j}|rLdd|jj|j|ddD}tt|j|}n t|j}t|dd|d|jdfS)NcSsh|] }|jqSr6rrrr6r6r7 zsz3RevisionStep.merge_branch_idents..Fcheckr)r differencerr_get_ancestor_nodesrlistr)r5r other_heads ancestorsrr6r6r7rts   z RevisionStep.merge_branch_identscszt|jjg}|rNddjjj|ddD}ttj|SfddjD}ttj|SdS)NcSsh|] }|jqSr6r r r6r6r7rsz5RevisionStep._unmerge_to_revisions..Frcs:h|]2}jjj|ddD]}|j|kr |jq qS)Fr)rrrr)rZ to_revisionr r;r6r7rs  )rrrrrrrr)r5rrrr6r;r7r s  z"RevisionStep._unmerge_to_revisionsz Tuple[str, str, Tuple[str, ...]]cCs&||}|jd|d|ddfSNrr)r r)r5rrr6r6r7rs   z"RevisionStep.unmerge_branch_identscCs0|js dS|jj}|sdS||s(dSdSdS)NFT)rrr intersectionr5rr r6r6r7rs z!RevisionStep.should_create_branchcCs8|js dS|jj}t|dkr4t||dkr4dSdSNFr'T)rrrrrrr6r6r7rs z"RevisionStep.should_merge_branchescCs2|js dS|jj}|jj|kr.t|dkr.dSdSr)rrrrrr6r6r7rs z$RevisionStep.should_unmerge_branchesTuple[str, str]cCsd|js6||jj}t|dks(tdt|d}n |jjd}|jrT||jjfS|jj|fSdS)Nr'z4Can't do an UPDATE because downrevision is ambiguousr)rrrrrr=rr)r5rZdownrevZ down_revisionr6r6r7rs   zRevisionStep.update_version_numrcCs|jjSr4r r;r6r6r7rszRevisionStep.delete_version_numcCs|jjSr4r r;r6r6r7rszRevisionStep.insert_version_numrcCst|j|jj|jj|jddS)NFrrrrr)rrrrrr;r6r6r7rjszRevisionStep.infoN)rGrHrIr8rrrJrrrrrrrrr rrrrrrrrjr6r6r6r7rs:    rc@s4eZdZUd8dddddddddZdZd ed <d dd d dZddZeddZ eddddZ eddddZ eddddZ eddddZ eddddZd d!d"d#d$Zd%d&d"d'd(Zd d)d"d*d+Zd dd"d,d-Zd d.d"d/d0Zd dd"d1d2Zd dd"d3d4Zed5dd6d7ZdS)9 StampStepNz%Optional[Union[str, Collection[str]]]r|zOptional[RevisionMap]r1)rrr branch_moverr3cCs>tj|dd|_tj|dd|_||_||_|j|_||_dS)Nr6r) rrrrrrstamp_revisionrr)r5rrrrrr6r6r7r8s zStampStep.__init__rlrrrcKsdSr4r6)r5rr6r6r7rszStampStep.stamp_revisioncCs:t|to8|j|jko8|j|jko8|j|jko8|j|jkSr4)rorrrrrrr6r6r7rs     zStampStep.__eq__cCs|jSr4rr;r6r6r7rszStampStep.from_revisionsrr9cCs|jSr4rr;r6r6r7r szStampStep.to_revisionscCs|jSr4r r;r6r6r7r$sz StampStep.from_revisions_no_depscCs|jSr4r!r;r6r6r7r*szStampStep.to_revisions_no_depsrcCst|jdkst|jdSNr'r)rrr=r;r6r6r7r0szStampStep.delete_version_numcCst|jdkst|jdSr")rrr=r;r6r6r7r5szStampStep.insert_version_numrrrcCs8t|jdkstt|jdks$t|jd|jdfSr")rrr=rrr6r6r7r:szStampStep.update_version_numzUnion[Set[str], List[str]]z=Union[Tuple[List[Any], str, str], Tuple[List[str], str, str]]cCs$t|jdd|jd|jdfSr)rrrrr6r6r7r?szStampStep.merge_branch_identszTuple[str, str, List[str]]cCs$|jd|jdt|jddfSr)rrrrr6r6r7rIszStampStep.unmerge_branch_identscCs |jo |jSr4)rrrr6r6r7rSszStampStep.should_delete_branchzUnion[Set[str], bool]cCs,|jo*|jst|j|o*t|j|Sr4)rrrrrrrr6r6r7rYs zStampStep.should_create_branchcCst|jdkSr)rrrr6r6r7r`szStampStep.should_merge_branchescCst|jdkSr)rrrr6r6r7rcsz!StampStep.should_unmerge_branchesrcCsF|jr|j|jfn |j|jf\}}|jdk s0tt|j|||jddS)NTr)rrrrr=r)r5upZdownr6r6r7rjfs zStampStep.info)N)rGrHrIr8rrrrrJrrrrrrrrrrrrrrjr6r6r6r7rs4      r)G __future__r contextlibrrloggingrbtypingrrrrr r r r r rrrrrZ sqlalchemyrrrrrrZsqlalchemy.enginerrrqZsqlalchemy.engine.strategiesrrrrZ util.compatr r!r"Zsqlalchemy.engine.baser#r$Zsqlalchemy.engine.mockr%Zsqlalchemy.sqlr& environmentr(rr)Z script.baser*r+Zscript.revisionr,r-r. getLoggerrGrir/r0rrrrrr6r6r6r7sx                                            $o3c