U ofV @sddlmZddlZddlmZddlmZddlmZddlmZddlmZddlm Z dd lm Z dd lm Z dd lm Z dd l mZd dlmZd dlmZddlmZddlmZddlmZe rddlmZddlmZddlmZddlmZddlmZddlmZddlmZddl m!Z!ddl m"Z"ddl m#Z#ddl$m%Z%ddl$m&Z&dd l$m'Z'dd!l$m(Z(dd"l)m*Z*dd#l+m,Z,dd$l+m-Z-dd%l.m/Z/d&d'd(d)d*d+Z0d&d'd,d)d-d.Z1dFd3d4d4d5d6d7d8d9d4d: d;d<Z2d&d=d>d?d@dAZ3GdBdCdCZ4GdDdEdEZ5dS)G) annotationsN)Any)Dict)Iterator)List)Optional)Sequence)Set) TYPE_CHECKING)Union)inspect)compare)render)util)ops) sqla_compat) Connection)Dialect) Inspector)MetaData) SchemaItem)Table)Config) DowngradeOps)MigrationScript) UpgradeOps)NameFilterParentNames)NameFilterType)ProcessRevisionDirectiveFn) RenderItemFnMigrationContext)Script)ScriptDirectory) _GetRevArgr#rr)contextmetadatareturncCs"t||}|jdk st|jS)aB Compare a database schema to that given in a :class:`~sqlalchemy.schema.MetaData` instance. The database connection is presented in the context of a :class:`.MigrationContext` object, which provides database connectivity as well as optional comparison functions to use for datatypes and server defaults - see the "autogenerate" arguments at :meth:`.EnvironmentContext.configure` for details on these. The return format is a list of "diff" directives, each representing individual differences:: from alembic.migration import MigrationContext from alembic.autogenerate import compare_metadata from sqlalchemy import ( create_engine, MetaData, Column, Integer, String, Table, text, ) import pprint engine = create_engine("sqlite://") with engine.begin() as conn: conn.execute( text( ''' create table foo ( id integer not null primary key, old_data varchar, x integer ) ''' ) ) conn.execute(text("create table bar (data varchar)")) metadata = MetaData() Table( "foo", metadata, Column("id", Integer, primary_key=True), Column("data", Integer), Column("x", Integer, nullable=False), ) Table("bat", metadata, Column("info", String)) mc = MigrationContext.configure(engine.connect()) diff = compare_metadata(mc, metadata) pprint.pprint(diff, indent=2, width=20) Output:: [ ( "add_table", Table( "bat", MetaData(), Column("info", String(), table=), schema=None, ), ), ( "remove_table", Table( "bar", MetaData(), Column("data", VARCHAR(), table=), schema=None, ), ), ( "add_column", None, "foo", Column("data", Integer(), table=), ), [ ( "modify_nullable", None, "foo", "x", { "existing_comment": None, "existing_server_default": False, "existing_type": INTEGER(), }, True, False, ) ], ( "remove_column", None, "foo", Column("old_data", VARCHAR(), table=), ), ] :param context: a :class:`.MigrationContext` instance. :param metadata: a :class:`~sqlalchemy.schema.MetaData` instance. .. seealso:: :func:`.produce_migrations` - produces a :class:`.MigrationScript` structure based on metadata comparison. N)produce_migrations upgrade_opsAssertionErrorZas_diffs)r'r(migration_scriptr.F/opt/hc_python/lib/python3.8/site-packages/alembic/autogenerate/api.pycompare_metadata/sy r0rcCs8t||d}tjdtgtgd}t|||S)aProduce a :class:`.MigrationScript` structure based on schema comparison. This function does essentially what :func:`.compare_metadata` does, but then runs the resulting list of diffs to produce the full :class:`.MigrationScript` object. For an example of what this looks like, see the example in :ref:`customizing_revision`. .. seealso:: :func:`.compare_metadata` - returns more fundamental "diff" data from comparing a schema. )r(Nrev_idr+ downgrade_ops)AutogenContextrrrrr_populate_migration_script)r'r(autogen_contextr-r.r.r/r*s  r*sa.op.Fr.zUnion[UpgradeOps, DowngradeOps]strboolz Sequence[str]zOptional[RenderItemFn]zOptional[MigrationContext] Optional[str]) up_or_down_opsqlalchemy_module_prefixalembic_module_prefixrender_as_batchimports render_itemmigration_contextuser_module_prefixr)c Csf|||||d}|dkr>ddlm} ddlm} | j| d}t||d} t|| _t t || S) a*Render Python code given an :class:`.UpgradeOps` or :class:`.DowngradeOps` object. This is a convenience function that can be used to test the autogenerate output of a user-defined :class:`.MigrationScript` structure. :param up_or_down_op: :class:`.UpgradeOps` or :class:`.DowngradeOps` object :param sqlalchemy_module_prefix: module prefix for SQLAlchemy objects :param alembic_module_prefix: module prefix for Alembic constructs :param render_as_batch: use "batch operations" style for rendering :param imports: sequence of import symbols to add :param render_item: callable to render items :param migration_context: optional :class:`.MigrationContext` :param user_module_prefix: optional string prefix for user-defined types .. versionadded:: 1.11.0 )r=r>rAr?rCNrr"r)DefaultDialect)dialect)opts) runtime.migrationr#Zsqlalchemy.engine.defaultrD configurer4setr@r_indentZ_render_cmd_body) r<r=r>r?r@rArBrCrFr#rDr6r.r.r/render_python_codes"     rKzDict[Any, Any]None)r' template_argsr)cCsDt|}tg}t||tjd||d}t|||dS)z6legacy, used by test_autogen_composition at the momentNr1) r4rrrZ_produce_net_changesrreverser _render_python_into_templatevars)r'rMr6r+r-r.r.r/_render_migration_diffss  rPc@seZdZUdZdZded<dZded<dZded<dZd ed <dZ d ed <d-d dddddddZ e j ddddZ ejddddZddddddd Zd!d"ddd#dd$d%d&ZeZe j d'dd(d)Ze j d*dd+d,ZdS).r4zSMaintains configuration and state that's specific to an autogenerate operation.NzOptional[MetaData]r(zOptional[Connection] connectionzOptional[Dialect]rEzSet[str]r@r#rBTzOptional[Dict[str, Any]]r:rL)rBr(rF autogenerater)c Cs|r|dk r|jrtd|dkr*|j}|dkr>|ddn||_}|rx|dkrx|dk rx|jdk rxtd|jj|dd}|dd}g}g}|r|||r||||_ ||_ ||_ |j dk r|j j |_ |j j|_t|_||_d|_dS)Nz^autogenerate can't use as_sql=True as it prevents querying the database for schema informationZtarget_metadatazCan't proceed with --autogenerate option; environment script %s does not provide a MetaData object or sequence of objects to the context.include_object include_nameF)Zas_sqlr CommandErrorrFgetr(scriptZenv_py_locationappend_object_filters _name_filtersrBbindrQrErIr@ _has_batch) selfrBr(rFrRrSrTZobject_filtersZ name_filtersr.r.r/__init__LsV       zAutogenContext.__init__rr)cCs|jdkrtdt|jS)NzHcan't return inspector as this AutogenContext has no database connection)rQ TypeErrorr r]r.r.r/ inspectors  zAutogenContext.inspectorzIterator[None]ccsd|_dVd|_dS)NTF)r\rar.r.r/ _within_batchszAutogenContext._within_batchr;rr)nametype_ parent_namesr)cCsnd|krL|dkr|}n |dd}|rL|d}|rDd||f|d<n||d<|jD]}||||sRdSqRdS) aRun the context's name filters and return True if the targets should be part of the autogenerate operation. This method should be run for every kind of name encountered within the reflection side of an autogenerate operation, giving the environment the chance to filter what names should be reflected as database objects. The filters here are produced directly via the :paramref:`.EnvironmentContext.configure.include_name` parameter. schema_nametable table_nameNz%s.%sZschema_qualified_table_nameFT)rVrZ)r]rdrerfrirgfnr.r.r/run_name_filterss     zAutogenContext.run_name_filtersrzsqla_compat._ConstraintNamezOptional[SchemaItem])object_rdre reflected compare_tor)cCs&|jD]}||||||sdSqdS)aRun the context's object filters and return True if the targets should be part of the autogenerate operation. This method should be run for every kind of object encountered within an autogenerate operation, giving the environment the chance to filter what objects should be included in the comparison. The filters here are produced directly via the :paramref:`.EnvironmentContext.configure.include_object` parameter. FTN)rY)r]rlrdrermrnrjr.r.r/run_object_filterss z!AutogenContext.run_object_filtersz List[Table]cCs&g}t|jD]}||jq|S)aiReturn an aggregate of the :attr:`.MetaData.sorted_tables` collection(s). For a sequence of :class:`.MetaData` objects, this concatenates the :attr:`.MetaData.sorted_tables` collection for each individual :class:`.MetaData` in the order of the sequence. It does **not** collate the sorted tables collections. )rto_listr(extend sorted_tables)r]resultmr.r.r/rrs zAutogenContext.sorted_tableszDict[str, Table]cCs^i}t|jD]H}t|t|j}|rLtddddt|D| |jq|S)aReturn an aggregate of the :attr:`.MetaData.tables` dictionaries. The :attr:`.MetaData.tables` collection is a dictionary of table key to :class:`.Table`; this method aggregates the dictionary across multiple :class:`.MetaData` objects into one dictionary. Duplicate table keys are **not** supported; if two :class:`.MetaData` objects contain the same table key, an exception is raised. z9Duplicate table keys across multiple MetaData objects: %sz, css|]}d|VqdS)z"%s"Nr.).0keyr.r.r/ sz4AutogenContext.table_key_to_table..) rrpr(rI intersectionZtables ValueErrorjoinsortedupdate)r]rsrtZ intersectr.r.r/table_key_to_tables z!AutogenContext.table_key_to_table)NNT)__name__ __module__ __qualname____doc__r(__annotations__rQrEr@rBr^rZmemoized_propertyrb contextlibcontextmanagerrcrkroZ run_filtersrrr}r.r.r.r/r4s*     ;%r4c@seZdZUdZded<ded<d$ddd dd d d d ZdddddZddd dddZddd dddZdddd dddZ dddd Z d!dd"d#Z dS)%RevisionContextz^Maintains configuration and state that's specific to a revision file generation operation.zList[MigrationScript]generated_revisionsz$Optional[ProcessRevisionDirectiveFn]process_revision_directivesNrr%zDict[str, Any]rL)configscript_directory command_argsrr)cCs2||_||_||_||_d|i|_|g|_dS)Nr)rrrrrM_default_revisionr)r]rrrrr.r.r/r^s zRevisionContext.__init__rzOptional[Script])r-r)c Cs|j}t|ddrF|j}t|_|jr8|j|jt||||j dk sTt |j j |j |j fd|j|j|j|j|jd|S)N _needs_renderFT)refreshheadspliceZ branch_labels version_path depends_on)rMcopygetattr_last_autogen_contextrIr@r|rrOr2r,rZgenerate_revisionmessagerr branch_labelrr)r]r-rMr6r.r.r/ _to_scripts2   zRevisionContext._to_scriptr&r#)revrBr)cCs|||ddS)NT_run_environmentr]rrBr.r.r/run_autogenerate7sz RevisionContext.run_autogeneratecCs|||ddS)NFrrr.r.r/run_no_autogenerate<sz#RevisionContext.run_no_autogenerater:)rrBrRr)c Cs&|rB|jdrtdt|j|t|jdkrBtd|jd}|jd}|jd}t|dd s||j d_ ||j d_ d |_ n,|jtjg|d |jtjg|d t||d }||_|rt|||jr||||j|jd}|r||||j|jD] }d |_ qdS)Nsqlz7Using --sql with --autogenerate does not make any senseZheadsz"Target database is not up to date. upgrade_tokendowngrade_tokenrFT)r)r)rRr)rrrUrIrZ get_revisionsrFrrZupgrade_ops_listrZdowngrade_ops_listrrZ _upgrade_opsrXrrZ_downgrade_opsrr4rrr5r) r]rrBrRrrr-r6hookr.r.r/rAsX             z RevisionContext._run_environmentr_c CsV|j}tj|dpt|dtgtg|d|d|d|d|dd }|S) Nr2rrrrrr) r2rr+r3rrrrr)rrrrr2rr)r]ropr.r.r/rys z!RevisionContext._default_revisionzIterator[Optional[Script]]ccs|jD]}||VqdS)N)rr)r]Zgenerated_revisionr.r.r/generate_scriptss z RevisionContext.generate_scripts)N) r~rrrrr^rrrrrrr.r.r.r/rs  8r)r7r8Fr.NNN)6 __future__rrtypingrrrrrrr r r Z sqlalchemyr rrr operationsrrZsqlalchemy.enginerrrZsqlalchemy.sql.schemarrrrrZoperations.opsrrrZruntime.environmentrrr r!rGr#Z script.baser$r%Zscript.revisionr&r0r*rKrPr4rr.r.r.r/sb                                  ~! 3l