<p dir="auto">I've just encountered this issue and after a few hours spent investigating I'm think I know what's going on.</p>
<p dir="auto">But first, some context:</p>
<p dir="auto">Let's say in my database I've got the following two tables:</p>
<ol dir="auto">
<li>base.customer</li>
<li>report.customer</li>
</ol>
<p dir="auto">I need to automap them both and, <strong>crucially, I want both classes to just be called 'customer'</strong>.</p>
<p dir="auto">I don't want to have to convert them to a python identifier using custom logic passed to the <code class="notranslate">AutomapBase.prepare(classname_for_table=)</code> argument. For example, the seemingly obvious solution would be to replace the dot with an underscore and name them <code class="notranslate">base_customer</code>, and <code class="notranslate">report_customer</code> respectively, to make them valid python identifiers. But this is inelegant, and there will always be ambiguity.</p>
<p dir="auto">What if I have a schema called <code class="notranslate">customer_view</code> with a table called <code class="notranslate">customer</code>, and another schema called <code class="notranslate">customer</code> with a table called <code class="notranslate">view_customer</code>? Both classes then get mapped as <code class="notranslate">customer_view_customer</code>. Maybe not the best example, but it should showcase the issue.</p>
<p dir="auto">You can spend forever coming up with ways to try to avoid this (maybe use 2 underscores instead of 1), and you still won't guarantee it works in every situation (some database schemas use double underscores in their object names as well to represent m2m relationships).</p>
<p dir="auto">And it will just make everything more needlessly complicated. If you do this you now have to change the <code class="notranslate">AutomapBase.prepare(name_for_scalar_relationship=, name_for_collection_relationship=)</code> arguments so that you can still reference relationships nicely: for example: <code class="notranslate">customer1.email_address_collection</code> rather than <code class="notranslate">customer.base_email_address_collection</code>, and <code class="notranslate">some_email.customer</code> rather than <code class="notranslate">some_email.base_customer</code>.</p>
<p dir="auto">The more elegant and idiomatic way to handle this seems to me to separate out the namespaces of schemas, to then allow duplication of names across different namespaces. As the Zen of Python states: "Namespaces are one honking great idea -- let's do more of those!"</p>
<p dir="auto">So what I've done is I've created a new <code class="notranslate">AutomapBase</code> for every schema in my database in a loop, and called <code class="notranslate">AutomapBase.prepare(schema=)</code> individually for each, passing the specific schema to each one. I've also used the <code class="notranslate">AutomapBase.prepare(classname_for_table=)</code> argument to ensure that the schema name is not included in the name of the newly mapped class.</p>
<p dir="auto">Now I can assign my <code class="notranslate">base_automap.classes</code> as <code class="notranslate">base</code>, my <code class="notranslate">report_automap.classes</code> as <code class="notranslate">report</code>, and then simply reference my classes as <code class="notranslate">base.customer</code> and <code class="notranslate">report.customer</code>, and it's so clean!</p>
<p dir="auto">The part that gets me is that this ALMOST works. In fact, it works like a dream so long as there are no tables across the whole database that have duplicate names.</p>
<p dir="auto">However, as soon as there are duplicates, the root <code class="notranslate">_ModuleMarker</code> (the one referenced as <code class="notranslate">_sa_module_registry</code> within each base's <code class="notranslate">_decl_class_registry</code>), through the <code class="notranslate">_ModuleMarker</code> of whichever module your <code class="notranslate">AutomapBase</code> was declared in (I think it's <code class="notranslate">sqlalchemy.ext.automap</code> unless you use a custom base), now has a <code class="notranslate">_MultipleClassMarker</code> against that table name in its <code class="notranslate">_ModuleMarker.contents</code> attribute, which will kill the old weakref as soon as a duplicate table name comes along.</p>
<p dir="auto">And without this weakref being alive, several downstream parts of the automapping machinery (such as the <code class="notranslate">_relationships_for_fks()</code> method) just fail gracelessly, because they rely on this weakref being alive to work.</p>
<p dir="auto">While googling around for this before posting this issue I've found this StackOverflow question (<a href="https://stackoverflow.com/questions/57118047/typeerror-issubclass-arg-2-must-be-a-class-or-tuple-of-classes" rel="nofollow">https://stackoverflow.com/questions/57118047/typeerror-issubclass-arg-2-must-be-a-class-or-tuple-of-classes</a>) which I believe stems from the same cause. While I can't be 100% certain, the poster of this question probably had a schema change be made to his/her database that caused there to be multiple tables with the same name across different schemas, so their automapping broke.</p>
<p dir="auto">This error is basically the same one I've been getting. The weakref-invoking property <code class="notranslate">_DeferredMapperConfig.cls</code> returns <code class="notranslate">None</code> from its dead weakref, which then causes an <code class="notranslate">issubclass()</code> check to receive <code class="notranslate">None</code> as its second argument, rather than a type. Many other methods called downstream of <code class="notranslate">AutomapBase.prepare()</code> also fail in similar ways due to the unexpected presence of <code class="notranslate">None</code>, rather than a base.</p>
<p dir="auto">And the part that's kind of hilarious is that just as the poster in that StackOverflow issue mentioned, sometimes it just randomly works! This does actually make sense, since Python makes no guarantees about when it garbage-collects unreferenced objects. So if you get lucky your reference might still be alive by the time the flow of execution gets to that point. Will it work? Won't it? Who knows! :)</p>
<p dir="auto">I'm wondering if we can get a patch in to the way <code class="notranslate">_MultipleClassMarker</code> works, so that it can keep track of multiple database objects with identical name stems (the part of the object name after the schema) simultaneously in a schema-aware manner?</p>
<p dir="auto">If that's too much effort then at minimum a small change keeping the weakrefs cached in this situation rather than killing them off should be a decent compromise, since it will mean the automapper can still finish doing its job, and the only additional memory costs incurred will be for tables with duplicate names, which should hopefully not be too many.</p>
<p dir="auto">In the long-term I think it would be preferable to add in better multi-schema support to <code class="notranslate">AutomapBase.prepare()</code> just in general, but that's a much bigger job. It's kind of silly that in order to namespace my automapped classes the same way we namespace our objects everywhere else in both Python and SQL, we need to use duplicate <code class="notranslate">AutomapBase</code> objects as a hacky workaround.</p>
<p dir="auto">Sorry for this wall of text, this really got away from me a bit.</p>
<p dir="auto">I'd be crazy grateful if this could be considered.</p>
<p dir="auto">Thanks for your time.</p>
<p dir="auto"><strong>Migrated issue, originally created by jek (<a href="https://github.com/jek">@jek</a>)</strong></p>
<p dir="auto">Implement generic MERGE, aka 'upsert'.  In ANSI, it looks like:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="MERGE INTO table_name1 USING table_name2 ON (condition)
WHEN MATCHED THEN UPDATE SET column1 = value1 [column2 = value2 ...](,)
WHEN NOT MATCHED THEN INSERT columns VALUES (values) "><pre class="notranslate"><code class="notranslate">MERGE INTO table_name1 USING table_name2 ON (condition)
WHEN MATCHED THEN UPDATE SET column1 = value1 [column2 = value2 ...](,)
WHEN NOT MATCHED THEN INSERT columns VALUES (values) 
</code></pre></div>
<p dir="auto">Dialect support differs pretty widely.  A quick &amp; likely inaccurate poll:</p>
<ul dir="auto">
<li>Oracle 9+ has a MERGE</li>
<li>t-sql 2008 has a MERGE, earlier can maybe do <code class="notranslate">IF EXISTS(SELECT ...)</code></li>
<li>MySQL is limited to a key violation condition, and can do either <code class="notranslate">INSERT ... ON DUPLICATE KEY UPDATE</code> or <code class="notranslate">REPLACE INTO</code>, INSERT being preferable</li>
<li>SQLite is limited to a key violation condition, and has <code class="notranslate">INSERT .. ON CONFLICT REPLACE</code></li>
</ul>