Skip to content

feat: fix SQLAlchemy 2.x compatibility in row iteration#4

Merged
nomeguy merged 6 commits intomasterfrom
copilot/fix-sqlalchemy-dependency-conflict
Nov 14, 2025
Merged

feat: fix SQLAlchemy 2.x compatibility in row iteration#4
nomeguy merged 6 commits intomasterfrom
copilot/fix-sqlalchemy-dependency-conflict

Conversation

Copy link
Contributor

Copilot AI commented Nov 14, 2025

Fix SQLAlchemy Version Conflict ✅

Problem Statement

The package previously pinned SQLAlchemy==1.4.42 and databases==0.7.0, causing dependency conflicts when trying to use the adapter alongside packages that require SQLAlchemy 2.x, such as sqlmodel>=0.0.27.

Solution Summary

  • Explored repository structure and dependencies
  • Identified compatibility issue between SQLAlchemy 1.4 and 2.x
  • Fixed adapter code to work with both versions
  • Updated dependency requirements to support wider version ranges
  • Ran security checks (CodeQL - no vulnerabilities found)
  • Verified backward and forward compatibility
  • Fixed Black linter formatting issues
  • Removed test_compatibility.py as requested

Changes Made

1. Code Changes (adapter.py)

Changed row._mapping.items() to dict(row._mapping).items() in two methods:

  • load_policy() - Line 32
  • load_filtered_policy() - Line 87

Why this change? In SQLAlchemy 2.x with databases>=0.8.0, accessing .items directly on row._mapping goes through __getattr__ which tries to look it up as a column name. Using dict() conversion works with both versions.

2. Dependency Updates

  • requirements.txt: Relaxed SQLAlchemy==1.4.42SQLAlchemy>=1.4.42
  • requirements.txt: Relaxed databases==0.7.0databases>=0.7.0
  • requirements-test.txt: Applied same version range updates

3. Code Formatting

  • Applied Black formatter style to adapter.py for CI compliance

4. Build Configuration

  • Updated .gitignore to exclude Python build artifacts (*.egg-info, dist/, build/)

Compatibility Matrix

SQLAlchemy databases Test Status
1.4.42 0.7.0 ✅ All 6 tests pass
2.0.44 0.9.0 ✅ All 6 tests pass

Verification

  • ✅ Backward compatible with SQLAlchemy 1.4.x
  • ✅ Forward compatible with SQLAlchemy 2.x
  • ✅ Can now be installed alongside sqlmodel 0.0.27 without conflicts
  • ✅ CodeQL security scan: No vulnerabilities found
  • ✅ Black linter: All files pass formatting checks

Minimal Impact

Only 2 lines of code changed in the adapter, ensuring minimal risk and maintaining existing behavior.

Original prompt

This section details on the original issue you should resolve

<issue_title>Dependency conflict with SQLAlchemy version (required 1.4.42)</issue_title>
<issue_description>Description
I ran into a dependency issue when trying to install casbin-databases-adapter with Poetry. The package currently pins SQLAlchemy to version 1.4.42, which causes a conflict when using sqlmodel (which requires SQLAlchemy >=2.0.14,<2.1.0).

Here is the error message I get:

Because no versions of casbin-databases-adapter match >1.5.0,<2.0.0
 and casbin-databases-adapter (1.5.0) depends on SQLAlchemy (1.4.42), casbin-databases-adapter (>=1.5.0,<2.0.0) requires SQLAlchemy (1.4.42).
And because sqlmodel (0.0.27) depends on SQLAlchemy (>=2.0.14,<2.1.0), casbin-databases-adapter (>=1.5.0,<2.0.0) is incompatible with sqlmodel (0.0.27).
So, because backend-ado depends on both sqlmodel (0.0.27) and casbin-databases-adapter (^1.5.0), version solving failed.

Would it be possible to relax the SQLAlchemy version requirement or update the package to support SQLAlchemy 2.x and the latest SQLModel releases?</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@nomeguy nomeguy marked this pull request as ready for review November 14, 2025 15:45
Copilot AI and others added 4 commits November 14, 2025 15:51
Co-authored-by: nomeguy <85475922+nomeguy@users.noreply.github.com>
Co-authored-by: nomeguy <85475922+nomeguy@users.noreply.github.com>
Co-authored-by: nomeguy <85475922+nomeguy@users.noreply.github.com>
Co-authored-by: nomeguy <85475922+nomeguy@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix dependency conflict with SQLAlchemy version Fix SQLAlchemy 2.x compatibility in row iteration Nov 14, 2025
Copilot AI requested a review from nomeguy November 14, 2025 16:03
@nomeguy
Copy link

nomeguy commented Nov 14, 2025

@copilot fix failed CI linter: https://github.com/officialpycasbin/casbin-databases-adapter/actions/runs/19370124141/job/55424203749

remove the file: test_compatibility.py

Run github/super-linter@v4.2.2
  env:
    VALIDATE_PYTHON_BLACK: true
    DEFAULT_BRANCH: master
    GITHUB_TOKEN: ***
/usr/bin/docker run --name ghcriogithubsuperlinterv422_4076e0 --label a6612a --workdir /github/workspace --rm -e "VALIDATE_PYTHON_BLACK" -e "DEFAULT_BRANCH" -e "GITHUB_TOKEN" -e "HOME" -e "GITHUB_JOB" -e "GITHUB_REF" -e "GITHUB_SHA" -e "GITHUB_REPOSITORY" -e "GITHUB_REPOSITORY_OWNER" -e "GITHUB_REPOSITORY_OWNER_ID" -e "GITHUB_RUN_ID" -e "GITHUB_RUN_NUMBER" -e "GITHUB_RETENTION_DAYS" -e "GITHUB_RUN_ATTEMPT" -e "GITHUB_ACTOR_ID" -e "GITHUB_ACTOR" -e "GITHUB_WORKFLOW" -e "GITHUB_HEAD_REF" -e "GITHUB_BASE_REF" -e "GITHUB_EVENT_NAME" -e "GITHUB_SERVER_URL" -e "GITHUB_API_URL" -e "GITHUB_GRAPHQL_URL" -e "GITHUB_REF_NAME" -e "GITHUB_REF_PROTECTED" -e "GITHUB_REF_TYPE" -e "GITHUB_WORKFLOW_REF" -e "GITHUB_WORKFLOW_SHA" -e "GITHUB_REPOSITORY_ID" -e "GITHUB_TRIGGERING_ACTOR" -e "GITHUB_WORKSPACE" -e "GITHUB_ACTION" -e "GITHUB_EVENT_PATH" -e "GITHUB_ACTION_REPOSITORY" -e "GITHUB_ACTION_REF" -e "GITHUB_PATH" -e "GITHUB_ENV" -e "GITHUB_STEP_SUMMARY" -e "GITHUB_STATE" -e "GITHUB_OUTPUT" -e "RUNNER_OS" -e "RUNNER_ARCH" -e "RUNNER_NAME" -e "RUNNER_ENVIRONMENT" -e "RUNNER_TOOL_CACHE" -e "RUNNER_TEMP" -e "RUNNER_WORKSPACE" -e "ACTIONS_RUNTIME_URL" -e "ACTIONS_RUNTIME_TOKEN" -e "ACTIONS_CACHE_URL" -e "ACTIONS_RESULTS_URL" -e GITHUB_ACTIONS=true -e CI=true -v "/var/run/docker.sock":"/var/run/docker.sock" -v "/home/runner/work/_temp/_github_home":"/github/home" -v "/home/runner/work/_temp/_github_workflow":"/github/workflow" -v "/home/runner/work/_temp/_runner_file_commands":"/github/file_commands" -v "/home/runner/work/casbin-databases-adapter/casbin-databases-adapter":"/github/workspace" ghcr.io/github/super-linter:v4.2.2
--------------------------------------------------------------------------------

                              /@@#///////@@/(@//@%/(@.@(       @@
                          @@//////////////////////////////#*  @@@
                        @////@//(///////////@@@@@///@//@/@**//@@(
                      @///////@///////////////@@@@    (           @,
                     @/(&/@////////////////////                     @
                    @////////////////////////@@                      @
                  @%////////(//////////%/////&@            @@       *,@           ______________
             @@@@@/@/#/////(&//////////////////                       .@         /              \
        *@@@@@.    .%///(//@//////////////////&.   .@@,                 @%      / Don't mind me  \
      @@%           .&@&&/@.@//&/////(//////////    @@@@@@@@@         .. &@    / I'm just looking \
    @@%               @@@@@   @&/////////////////#   @/       V  @@/ ,@@@ @   <  for some trash... |
@@@%                   @@@@        .%@@@@//////#@ @   @@         @     .,.     \__________________/
                                          @@@/@(  (@@@@% @/\      %
                                           @@@@(    .     .@@/\   #
                                             @                  %@%

--------------------------------------------------------------------------------
2025-11-14 16:06:54 [INFO]   ---------------------------------------------
2025-11-14 16:06:54 [INFO]   --- GitHub Actions Multi Language Linter ----
2025-11-14 16:06:54 [INFO]    - Image Creation Date:[2021-06-10T18:15:15Z]
2025-11-14 16:06:54 [INFO]    - Image Revision:[c4280ea4e19a1d73ba41dbf44ea5de0b0dc165ce]
2025-11-14 16:06:54 [INFO]    - Image Version:[c4280ea4e19a1d73ba41dbf44ea5de0b0dc165ce]
2025-11-14 16:06:54 [INFO]   ---------------------------------------------
2025-11-14 16:06:54 [INFO]   ---------------------------------------------
2025-11-14 16:06:54 [INFO]   The Super-Linter source code can be found at:
2025-11-14 16:06:54 [INFO]    - https://github.com/github/super-linter
2025-11-14 16:06:54 [INFO]   ---------------------------------------------
2025-11-14 16:06:54 [INFO]   --------------------------------------------
2025-11-14 16:06:54 [INFO]   Gathering GitHub information...
2025-11-14 16:06:54 [INFO]   Successfully found:[GITHUB_SHA], value:[744d4d12cc11b713c3ac2647322a6275bb1b72c0]
2025-11-14 16:06:54 [INFO]   Successfully found:[GITHUB_WORKSPACE], value:[/github/workspace]
2025-11-14 16:06:54 [INFO]   Successfully found:[GITHUB_EVENT_PATH], value:[/github/workflow/event.json]
2025-11-14 16:06:54 [INFO]   Successfully found:[GITHUB_ORG], value:[officialpycasbin]
2025-11-14 16:06:54 [INFO]   Successfully found:[GITHUB_REPO], value:[casbin-databases-adapter]
2025-11-14 16:06:54 [INFO]   Successfully found:[GITHUB_TOKEN]
2025-11-14 16:06:54 [INFO]   Successfully found:[GITHUB_REPOSITORY], value:[officialpycasbin/casbin-databases-adapter]
2025-11-14 16:06:54 [INFO]   Successfully found:[GITHUB_RUN_ID], value:[19370124141]
2025-11-14 16:06:54 [INFO]   --------------------------------------------
2025-11-14 16:06:54 [INFO]   Gathering user validation information...
2025-11-14 16:06:54 [INFO]   - Validating ALL files in code base...
2025-11-14 16:06:57 [INFO]   ---------------------------------
2025-11-14 16:06:57 [INFO]   ------ File list to check: ------
2025-11-14 16:06:57 [INFO]   ---------------------------------
2025-11-14 16:06:57 [INFO]   ----------------------------------------------
2025-11-14 16:06:57 [INFO]   Successfully gathered list of files...
2025-11-14 16:06:58 [INFO]   
2025-11-14 16:06:58 [INFO]   ----------------------------------------------
2025-11-14 16:06:58 [INFO]   ----------------------------------------------
2025-11-14 16:06:58 [INFO]   Linting [PYTHON_BLACK] files...
2025-11-14 16:06:58 [INFO]   ----------------------------------------------
2025-11-14 16:06:58 [INFO]   ----------------------------------------------
2025-11-14 16:06:58 [INFO]   ---------------------------
2025-11-14 16:06:58 [INFO]   File:[/github/workspace/casbin_databases_adapter/__init__.py]
2025-11-14 16:06:58 [INFO]    - File:[__init__.py] was linted with [black] successfully
2025-11-14 16:06:58 [INFO]   ---------------------------
2025-11-14 16:06:58 [INFO]   File:[/github/workspace/casbin_databases_adapter/adapter.py]
2025-11-14 16:06:58 [ERROR]   Found errors in [black] linter!
2025-11-14 16:06:58 [ERROR]   Error code: 1. Command output:
------
would reformat /github/workspace/casbin_databases_adapter/adapter.py
Oh no! 💥 💔 💥
1 file would be reformatted.
--- /github/workspace/casbin_databases_adapter/adapter.py	2025-11-14 16:06:53.638984 +0000
+++ /github/workspace/casbin_databases_adapter/adapter.py	2025-11-14 16:06:58.854498 +0000
@@ -27,11 +27,13 @@
         query = self.table.select()
         rows = await self.db.fetch_all(query)
         for row in rows:
             # convert row from tuple to csv format and removing the first column (id)
             line = [
-                v for k, v in dict(row._mapping).items() if k in self.cols and v is not None
+                v
+                for k, v in dict(row._mapping).items()
+                if k in self.cols and v is not None
             ]
             persist.load_policy_line(", ".join(line), model)
 
     async def save_policy(self, model: Model):
         await self.db.execute(self.table.delete())
@@ -82,11 +84,13 @@
                 query = query.where(self.table.columns[att].in_(value))
         rows = await self.db.fetch_all(query)
         for row in rows:
             # convert row from tuple to csv format and removing the first column (id)
             line = [
-                v for k, v in dict(row._mapping).items() if k in self.cols and v is not None
+                v
+                for k, v in dict(row._mapping).items()
+                if k in self.cols and v is not None
             ]
             persist.load_policy_line(", ".join(line), model)
 
     def is_filtered(self):
         return self.filtered
------
2025-11-14 16:06:58 [INFO]   ---------------------------
2025-11-14 16:06:58 [INFO]   File:[/github/workspace/setup.py]
2025-11-14 16:06:59 [INFO]    - File:[setup.py] was linted with [black] successfully
2025-11-14 16:06:59 [INFO]   ---------------------------
2025-11-14 16:06:59 [INFO]   File:[/github/workspace/tests/conftest.py]
2025-11-14 16:06:59 [INFO]    - File:[conftest.py] was linted with [black] successfully
2025-11-14 16:06:59 [INFO]   ---------------------------
2025-11-14 16:06:59 [INFO]   File:[/github/workspace/tests/test_adapter.py]
2025-11-14 16:06:59 [INFO]    - File:[test_adapter.py] was linted with [black] successfully
2025-11-14 16:06:59 [INFO]   ---------------------------
2025-11-14 16:06:59 [INFO]   File:[/github/workspace/tests/test_compatibility.py]
2025-11-14 16:06:59 [ERROR]   Found errors in [black] linter!
2025-11-14 16:06:59 [ERROR]   Error code: 1. Command output:
------
would reformat /github/workspace/tests/test_compatibility.py
Oh no! 💥 💔 💥
1 file would be reformatted.
--- /github/workspace/tests/test_compatibility.py	2025-11-14 16:06:53.639984 +0000
+++ /github/workspace/tests/test_compatibility.py	2025-11-14 16:06:59.768751 +0000
@@ -9,13 +9,16 @@
 
 
 async def test_sqlalchemy_version_info(db: Database, enforcer: Enforcer):
     """Test that reports which SQLAlchemy version is being used."""
     print(f"
Testing with SQLAlchemy version: {sqlalchemy.__version__}")
-    
+
     # Verify basic enforcement works
     assert enforcer.enforce("alice", "data1", "read") == True
     assert enforcer.enforce("bob", "data2", "write") == True
-    
+
     # This test should pass with both SQLAlchemy 1.4.x and 2.x
-    major_version = int(sqlalchemy.__version__.split('.')[0])
-    assert major_version in [1, 2], f"Unexpected SQLAlchemy version: {sqlalchemy.__version__}"
+    major_version = int(sqlalchemy.__version__.split(".")[0])
+    assert major_version in [
+        1,
+        2,
+    ], f"Unexpected SQLAlchemy version: {sqlalchemy.__version__}"
------
2025-11-14 16:07:00 [INFO]   ----------------------------------------------
2025-11-14 16:07:00 [INFO]   ----------------------------------------------
2025-11-14 16:07:00 [INFO]   The script has completed
2025-11-14 16:07:00 [INFO]   ----------------------------------------------
2025-11-14 16:07:00 [INFO]   ----------------------------------------------
2025-11-14 16:07:00 [ERROR]   ERRORS FOUND in PYTHON_BLACK:[2]
2025-11-14 16:07:00 [FATAL]   Exiting with errors found!

Co-authored-by: nomeguy <85475922+nomeguy@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Nov 14, 2025

@copilot fix failed CI linter: https://github.com/officialpycasbin/casbin-databases-adapter/actions/runs/19370124141/job/55424203749

remove the file: test_compatibility.py
...

Fixed in commit 92699be:

  • Applied Black formatter style to adapter.py (split long list comprehensions across multiple lines)
  • Removed test_compatibility.py file as requested
  • All 6 tests pass and Black linter now passes on all files

@nomeguy nomeguy changed the title Fix SQLAlchemy 2.x compatibility in row iteration feat: fix SQLAlchemy 2.x compatibility in row iteration Nov 14, 2025
@nomeguy nomeguy merged commit 944e081 into master Nov 14, 2025
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dependency conflict with SQLAlchemy version (required 1.4.42)

2 participants