Skip to content

CHORE: Add OneBranch release pipelines for mssql-python-odbc#664

Open
jahnvi480 wants to merge 33 commits into
mainfrom
jahnvi/odbc-release-pipelines
Open

CHORE: Add OneBranch release pipelines for mssql-python-odbc#664
jahnvi480 wants to merge 33 commits into
mainfrom
jahnvi/odbc-release-pipelines

Merge branch 'jahnvi/odbc-release-pipelines' of https://github.com/mi…

3caadc7
Select commit
Loading
Failed to load commit list.
Azure Pipelines / MSSQL-Python-PR-Validation failed Jul 17, 2026 in 55m 44s

Build #pr-validation-pipeline had test failures

Details

Tests

  • Failed: 1 (0.00%)
  • Passed: 28,234 (96.92%)
  • Other: 895 (3.07%)
  • Total: 29,130
Code coverage

  • 6743 of 8343 line covered (80.82%)

Annotations

Check failure on line 123 in Build log

See this annotation in the file changed.

@azure-pipelines azure-pipelines / MSSQL-Python-PR-Validation

Build log #L123

Bash exited with code '1'.

Check failure on line 135 in Build log

See this annotation in the file changed.

@azure-pipelines azure-pipelines / MSSQL-Python-PR-Validation

Build log #L135

The task has timed out.

Check failure on line 2368 in Build log

See this annotation in the file changed.

@azure-pipelines azure-pipelines / MSSQL-Python-PR-Validation

Build log #L2368

Bash exited with code '1'.

Check failure on line 15 in Build log

See this annotation in the file changed.

@azure-pipelines azure-pipelines / MSSQL-Python-PR-Validation

Build log #L15

Bash exited with code '1'.

Check failure on line 1 in test_cleanup_connections_weakset_modification_during_iteration

See this annotation in the file changed.

@azure-pipelines azure-pipelines / MSSQL-Python-PR-Validation

test_cleanup_connections_weakset_modification_during_iteration

AssertionError: Test failed. stderr: 
assert -11 == 0
 +  where -11 = CompletedProcess(args=['/opt/venv/bin/python', '-c', '\nimport mssql_python\nimport weakref\nimport gc\n\nclass TestConnection:\n    def __init__(self, conn_id):\n        self.conn_id = conn_id\n        self._closed = False\n\n    def close(self):\n        self._closed = True\n\n# Create connections with only weak references so they can be GC\'d easily\nweak_refs = []\nfor i in range(10):\n    conn = TestConnection(i)\n    mssql_python._register_connection(conn)\n    weak_refs.append(weakref.ref(conn))\n    # Don\'t keep strong reference - only weak_refs list has refs\n\ninitial_size = len(mssql_python._active_connections)\nprint(f"Initial WeakSet size: {initial_size}")\n\n# TEST 1: Demonstrate that direct iteration would be unsafe\n# (We can\'t actually do this in the real code, but we can show the principle)\nprint("TEST 1: Verifying list copy is necessary...")\n\n# Force some garbage collection\ngc.collect()\nafter_gc_size = len(mssql_python._active_connections)\nprint(f"WeakSet size after GC: {after_gc_size}")\n\n# TEST 2: Verify list copy allows safe iteration\nprint("TEST 2: Testing list copy creates stable snapshot...")\n\n# This is what _cleanup_connections does - creates a list copy\nwith mssql_python._connections_lock:\n    connections_to_close = list(mssql_python._active_connections)\n\nsnapshot_size = len(connections_to_close)\nprint(f"Snapshot list size: {snapshot_size}")\n\n# Now cause more GC while we iterate the snapshot\ngc.collect()\n\n# Iterate the snapshot - this should work even though WeakSet may have changed\nprocessed = 0\nfor conn in connections_to_close:\n    try:\n        if hasattr(conn, "_closed") and not conn._closed:\n            conn.close()\n        processed += 1\n    except Exception:\n        # Connection may have been GC\'d, that\'s OK\n        pass\n\nfinal_size = len(mssql_python._active_connections)\nprint(f"Final WeakSet size: {final_size}")\nprint(f"Processed {processed} connections from snapshot")\n\n# Key assertion: We could iterate the full snapshot even if WeakSet changed\nassert processed == snapshot_size, f"Should process all snapshot items: {processed} == {snapshot_size}"\n\nprint("WeakSet modification during iteration: PASSED")\nprint("[OK] list() copy prevents \'set changed size during iteration\' errors")\n'], returncode=-11, stdout='', stderr='').returncode
Raw output
self = <test_013_SqlHandle_free_shutdown.TestHandleFreeShutdown object at 0x5508161110>
conn_str = 'Server=172.17.0.4;Database=TestDB;Uid=SA;Pwd=Azure@123!;TrustServerCertificate=yes'

    def test_cleanup_connections_weakset_modification_during_iteration(self, conn_str):
        """
        Test that list copy prevents RuntimeError when WeakSet is modified during iteration.
    
        This is a more aggressive test of the connections_to_close = list(_active_connections) line.
    
        Validates that:
        1. Without the list copy, iterating WeakSet directly would fail if modified
        2. With the list copy, iteration is safe even if WeakSet shrinks due to GC
        3. The pattern prevents "dictionary changed size during iteration" type errors
        """
        script = textwrap.dedent(f"""
            import mssql_python
            import weakref
            import gc
    
            class TestConnection:
                def __init__(self, conn_id):
                    self.conn_id = conn_id
                    self._closed = False
    
                def close(self):
                    self._closed = True
    
            # Create connections with only weak references so they can be GC'd easily
            weak_refs = []
            for i in range(10):
                conn = TestConnection(i)
                mssql_python._register_connection(conn)
                weak_refs.append(weakref.ref(conn))
                # Don't keep strong reference - only weak_refs list has refs
    
            initial_size = len(mssql_python._active_connections)
            print(f"Initial WeakSet size: {{initial_size}}")
    
            # TEST 1: Demonstrate that direct iteration would be unsafe
            # (We can't actually do this in the real code, but we can show the principle)
            print("TEST 1: Verifying list copy is necessary...")
    
            # Force some garbage collection
            gc.collect()
            after_gc_size = len(mssql_python._active_connections)
            print(f"WeakSet size after GC: {{after_gc_size}}")
    
            # TEST 2: Verify list copy allows safe iteration
            print("TEST 2: Testing list copy creates stable snapshot...")
    
            # This is what _cleanup_connections does - creates a list copy
            with mssql_python._connections_lock:
                connections_to_close = list(mssql_python._active_connections)
    
            snapshot_size = len(connections_to_close)
            print(f"Snapshot list size: {{snapshot_size}}")
    
            # Now cause more GC while we iterate the snapshot
            gc.collect()
    
            # Iterate the snapshot - this should work even though WeakSet may have changed
            processed = 0
            for conn in connections_to_close:
                try:
                    if hasattr(conn, "_closed") and not conn._closed:
                        conn.close()
                    processed += 1
                except Exception:
                    # Connection may have been GC'd, that's OK
                    pass
    
            final_size = len(mssql_python._active_connections)
            print(f"Final WeakSet size: {{final_size}}")
            print(f"Processed {{processed}} connections from snapshot")
    
            # Key assertion: We could iterate the full snapshot even if WeakSet changed
            assert processed == snapshot_size, f"Should process all snapshot items: {{processed}} == {{snapshot_size}}"
    
            print("WeakSet modification during iteration: PASSED")
            print("[OK] list() copy prevents 'set changed size during iteration' errors")
        """)
    
        result = subprocess.run(
            [sys.executable, "-c", script], capture_output=True, text=True, timeout=15
        )
    
>      
... [The stack trace has been truncated as it exceeded the maximum allowed size. Please refer to the complete log available in the Test Run attachments for full details.]