cloud-sql-python-connector 1.12.1__tar.gz → 1.14.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. {cloud_sql_python_connector-1.12.1/cloud_sql_python_connector.egg-info → cloud_sql_python_connector-1.14.0}/PKG-INFO +85 -15
  2. cloud_sql_python_connector-1.12.1/PKG-INFO → cloud_sql_python_connector-1.14.0/README.md +81 -46
  3. cloud_sql_python_connector-1.12.1/README.md → cloud_sql_python_connector-1.14.0/cloud_sql_python_connector.egg-info/PKG-INFO +116 -11
  4. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/cloud_sql_python_connector.egg-info/SOURCES.txt +1 -0
  5. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/cloud_sql_python_connector.egg-info/requires.txt +1 -1
  6. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/google/cloud/sql/connector/asyncpg.py +1 -0
  7. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/google/cloud/sql/connector/client.py +4 -4
  8. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/google/cloud/sql/connector/connection_info.py +2 -2
  9. cloud_sql_python_connector-1.14.0/google/cloud/sql/connector/connection_name.py +51 -0
  10. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/google/cloud/sql/connector/connector.py +21 -34
  11. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/google/cloud/sql/connector/instance.py +17 -34
  12. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/google/cloud/sql/connector/lazy.py +12 -10
  13. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/google/cloud/sql/connector/pg8000.py +1 -0
  14. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/google/cloud/sql/connector/pymysql.py +1 -0
  15. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/google/cloud/sql/connector/pytds.py +1 -0
  16. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/google/cloud/sql/connector/refresh_utils.py +3 -3
  17. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/google/cloud/sql/connector/utils.py +2 -4
  18. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/google/cloud/sql/connector/version.py +1 -1
  19. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/setup.py +3 -3
  20. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/LICENSE +0 -0
  21. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/cloud_sql_python_connector.egg-info/dependency_links.txt +0 -0
  22. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/cloud_sql_python_connector.egg-info/not-zip-safe +0 -0
  23. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/cloud_sql_python_connector.egg-info/top_level.txt +0 -0
  24. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/google/cloud/sql/connector/__init__.py +0 -0
  25. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/google/cloud/sql/connector/enums.py +0 -0
  26. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/google/cloud/sql/connector/exceptions.py +0 -0
  27. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/google/cloud/sql/connector/py.typed +0 -0
  28. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/google/cloud/sql/connector/rate_limiter.py +0 -0
  29. {cloud_sql_python_connector-1.12.1 → cloud_sql_python_connector-1.14.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cloud-sql-python-connector
3
- Version: 1.12.1
3
+ Version: 1.14.0
4
4
  Summary: The Cloud SQL Python Connector is a library that can be used alongside a database driver to allow users with sufficient permissions to connect to a Cloud SQL database without having to manually allowlist IPs or manage SSL certificates.
5
5
  Home-page: https://github.com/GoogleCloudPlatform/cloud-sql-python-connector
6
6
  Author: Google LLC
@@ -11,12 +11,12 @@ Classifier: Development Status :: 5 - Production/Stable
11
11
  Classifier: Intended Audience :: Developers
12
12
  Classifier: License :: OSI Approved :: Apache Software License
13
13
  Classifier: Programming Language :: Python
14
- Classifier: Programming Language :: Python :: 3.8
15
14
  Classifier: Programming Language :: Python :: 3.9
16
15
  Classifier: Programming Language :: Python :: 3.10
17
16
  Classifier: Programming Language :: Python :: 3.11
18
17
  Classifier: Programming Language :: Python :: 3.12
19
- Requires-Python: >=3.8
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Requires-Python: >=3.9
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  Requires-Dist: aiofiles
@@ -31,7 +31,7 @@ Requires-Dist: pg8000>=1.31.1; extra == "pg8000"
31
31
  Provides-Extra: pytds
32
32
  Requires-Dist: python-tds>=1.15.0; extra == "pytds"
33
33
  Provides-Extra: asyncpg
34
- Requires-Dist: asyncpg>=0.29.0; extra == "asyncpg"
34
+ Requires-Dist: asyncpg>=0.30.0; extra == "asyncpg"
35
35
 
36
36
  <p align="center">
37
37
  <a href="https://cloud.google.com/blog/topics/developers-practitioners/how-connect-cloud-sql-using-python-easy-way">
@@ -537,6 +537,42 @@ The `create_async_connector` allows all the same input arguments as the
537
537
  Once a `Connector` object is returned by `create_async_connector` you can call
538
538
  its `connect_async` method, just as you would the `connect` method:
539
539
 
540
+ #### Asyncpg Connection Pool
541
+
542
+ ```python
543
+ import asyncpg
544
+ from google.cloud.sql.connector import Connector, create_async_connector
545
+
546
+ async def main():
547
+ # initialize Connector object for connections to Cloud SQL
548
+ connector = create_async_connector()
549
+
550
+ # creation function to generate asyncpg connections as the 'connect' arg
551
+ async def getconn(instance_connection_name, **kwargs) -> asyncpg.Connection:
552
+ return await connector.connect_async(
553
+ instance_connection_name,
554
+ "asyncpg",
555
+ user="my-user",
556
+ password="my-password",
557
+ db="my-db",
558
+ **kwargs, # ... additional asyncpg args
559
+ )
560
+
561
+ # initialize connection pool
562
+ pool = await asyncpg.create_pool(
563
+ "my-project:my-region:my-instance", connect=getconn
564
+ )
565
+
566
+ # acquire connection and query Cloud SQL database
567
+ async with pool.acquire() as conn:
568
+ res = await conn.fetch("SELECT NOW()")
569
+
570
+ # close Connector
571
+ await connector.close_async()
572
+ ```
573
+
574
+ #### SQLAlchemy Async Engine
575
+
540
576
  ```python
541
577
  import asyncpg
542
578
 
@@ -546,7 +582,7 @@ from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
546
582
  from google.cloud.sql.connector import Connector, create_async_connector
547
583
 
548
584
  async def init_connection_pool(connector: Connector) -> AsyncEngine:
549
- # initialize Connector object for connections to Cloud SQL
585
+ # creation function to generate asyncpg connections as 'async_creator' arg
550
586
  async def getconn() -> asyncpg.Connection:
551
587
  conn: asyncpg.Connection = await connector.connect_async(
552
588
  "project:region:instance", # Cloud SQL instance connection name
@@ -599,6 +635,40 @@ calls to `connector.close_async()` to cleanup resources.
599
635
  > This alternative requires that the running event loop be
600
636
  > passed in as the `loop` argument to `Connector()`.
601
637
 
638
+ #### Asyncpg Connection Pool
639
+
640
+ ```python
641
+ import asyncpg
642
+ from google.cloud.sql.connector import Connector, create_async_connector
643
+
644
+ async def main():
645
+ # initialize Connector object for connections to Cloud SQL
646
+ loop = asyncio.get_running_loop()
647
+ async with Connector(loop=loop) as connector:
648
+
649
+ # creation function to generate asyncpg connections as the 'connect' arg
650
+ async def getconn(instance_connection_name, **kwargs) -> asyncpg.Connection:
651
+ return await connector.connect_async(
652
+ instance_connection_name,
653
+ "asyncpg",
654
+ user="my-user",
655
+ password="my-password",
656
+ db="my-db",
657
+ **kwargs, # ... additional asyncpg args
658
+ )
659
+
660
+ # create connection pool
661
+ pool = await asyncpg.create_pool(
662
+ "my-project:my-region:my-instance", connect=getconn
663
+ )
664
+
665
+ # acquire connection and query Cloud SQL database
666
+ async with pool.acquire() as conn:
667
+ res = await conn.fetch("SELECT NOW()")
668
+ ```
669
+
670
+ #### SQLAlchemy Async Engine
671
+
602
672
  ```python
603
673
  import asyncio
604
674
  import asyncpg
@@ -609,17 +679,17 @@ from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
609
679
  from google.cloud.sql.connector import Connector
610
680
 
611
681
  async def init_connection_pool(connector: Connector) -> AsyncEngine:
612
- # initialize Connector object for connections to Cloud SQL
682
+ # creation function to generate asyncpg connections as 'async_creator' arg
613
683
  async def getconn() -> asyncpg.Connection:
614
- conn: asyncpg.Connection = await connector.connect_async(
615
- "project:region:instance", # Cloud SQL instance connection name
616
- "asyncpg",
617
- user="my-user",
618
- password="my-password",
619
- db="my-db-name"
620
- # ... additional database driver args
621
- )
622
- return conn
684
+ conn: asyncpg.Connection = await connector.connect_async(
685
+ "project:region:instance", # Cloud SQL instance connection name
686
+ "asyncpg",
687
+ user="my-user",
688
+ password="my-password",
689
+ db="my-db-name"
690
+ # ... additional database driver args
691
+ )
692
+ return conn
623
693
 
624
694
  # The Cloud SQL Python Connector can be used along with SQLAlchemy using the
625
695
  # 'async_creator' argument to 'create_async_engine'
@@ -1,38 +1,3 @@
1
- Metadata-Version: 2.1
2
- Name: cloud-sql-python-connector
3
- Version: 1.12.1
4
- Summary: The Cloud SQL Python Connector is a library that can be used alongside a database driver to allow users with sufficient permissions to connect to a Cloud SQL database without having to manually allowlist IPs or manage SSL certificates.
5
- Home-page: https://github.com/GoogleCloudPlatform/cloud-sql-python-connector
6
- Author: Google LLC
7
- Author-email: googleapis-packages@google.com
8
- License: Apache 2.0
9
- Platform: Posix; MacOS X; Windows
10
- Classifier: Development Status :: 5 - Production/Stable
11
- Classifier: Intended Audience :: Developers
12
- Classifier: License :: OSI Approved :: Apache Software License
13
- Classifier: Programming Language :: Python
14
- Classifier: Programming Language :: Python :: 3.8
15
- Classifier: Programming Language :: Python :: 3.9
16
- Classifier: Programming Language :: Python :: 3.10
17
- Classifier: Programming Language :: Python :: 3.11
18
- Classifier: Programming Language :: Python :: 3.12
19
- Requires-Python: >=3.8
20
- Description-Content-Type: text/markdown
21
- License-File: LICENSE
22
- Requires-Dist: aiofiles
23
- Requires-Dist: aiohttp
24
- Requires-Dist: cryptography>=42.0.0
25
- Requires-Dist: Requests
26
- Requires-Dist: google-auth>=2.28.0
27
- Provides-Extra: pymysql
28
- Requires-Dist: PyMySQL>=1.1.0; extra == "pymysql"
29
- Provides-Extra: pg8000
30
- Requires-Dist: pg8000>=1.31.1; extra == "pg8000"
31
- Provides-Extra: pytds
32
- Requires-Dist: python-tds>=1.15.0; extra == "pytds"
33
- Provides-Extra: asyncpg
34
- Requires-Dist: asyncpg>=0.29.0; extra == "asyncpg"
35
-
36
1
  <p align="center">
37
2
  <a href="https://cloud.google.com/blog/topics/developers-practitioners/how-connect-cloud-sql-using-python-easy-way">
38
3
  <img src="https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-sql-python-connector/main/docs/images/cloud-sql-python-connector.png" alt="cloud-sql-python-connector image">
@@ -537,6 +502,42 @@ The `create_async_connector` allows all the same input arguments as the
537
502
  Once a `Connector` object is returned by `create_async_connector` you can call
538
503
  its `connect_async` method, just as you would the `connect` method:
539
504
 
505
+ #### Asyncpg Connection Pool
506
+
507
+ ```python
508
+ import asyncpg
509
+ from google.cloud.sql.connector import Connector, create_async_connector
510
+
511
+ async def main():
512
+ # initialize Connector object for connections to Cloud SQL
513
+ connector = create_async_connector()
514
+
515
+ # creation function to generate asyncpg connections as the 'connect' arg
516
+ async def getconn(instance_connection_name, **kwargs) -> asyncpg.Connection:
517
+ return await connector.connect_async(
518
+ instance_connection_name,
519
+ "asyncpg",
520
+ user="my-user",
521
+ password="my-password",
522
+ db="my-db",
523
+ **kwargs, # ... additional asyncpg args
524
+ )
525
+
526
+ # initialize connection pool
527
+ pool = await asyncpg.create_pool(
528
+ "my-project:my-region:my-instance", connect=getconn
529
+ )
530
+
531
+ # acquire connection and query Cloud SQL database
532
+ async with pool.acquire() as conn:
533
+ res = await conn.fetch("SELECT NOW()")
534
+
535
+ # close Connector
536
+ await connector.close_async()
537
+ ```
538
+
539
+ #### SQLAlchemy Async Engine
540
+
540
541
  ```python
541
542
  import asyncpg
542
543
 
@@ -546,7 +547,7 @@ from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
546
547
  from google.cloud.sql.connector import Connector, create_async_connector
547
548
 
548
549
  async def init_connection_pool(connector: Connector) -> AsyncEngine:
549
- # initialize Connector object for connections to Cloud SQL
550
+ # creation function to generate asyncpg connections as 'async_creator' arg
550
551
  async def getconn() -> asyncpg.Connection:
551
552
  conn: asyncpg.Connection = await connector.connect_async(
552
553
  "project:region:instance", # Cloud SQL instance connection name
@@ -599,6 +600,40 @@ calls to `connector.close_async()` to cleanup resources.
599
600
  > This alternative requires that the running event loop be
600
601
  > passed in as the `loop` argument to `Connector()`.
601
602
 
603
+ #### Asyncpg Connection Pool
604
+
605
+ ```python
606
+ import asyncpg
607
+ from google.cloud.sql.connector import Connector, create_async_connector
608
+
609
+ async def main():
610
+ # initialize Connector object for connections to Cloud SQL
611
+ loop = asyncio.get_running_loop()
612
+ async with Connector(loop=loop) as connector:
613
+
614
+ # creation function to generate asyncpg connections as the 'connect' arg
615
+ async def getconn(instance_connection_name, **kwargs) -> asyncpg.Connection:
616
+ return await connector.connect_async(
617
+ instance_connection_name,
618
+ "asyncpg",
619
+ user="my-user",
620
+ password="my-password",
621
+ db="my-db",
622
+ **kwargs, # ... additional asyncpg args
623
+ )
624
+
625
+ # create connection pool
626
+ pool = await asyncpg.create_pool(
627
+ "my-project:my-region:my-instance", connect=getconn
628
+ )
629
+
630
+ # acquire connection and query Cloud SQL database
631
+ async with pool.acquire() as conn:
632
+ res = await conn.fetch("SELECT NOW()")
633
+ ```
634
+
635
+ #### SQLAlchemy Async Engine
636
+
602
637
  ```python
603
638
  import asyncio
604
639
  import asyncpg
@@ -609,17 +644,17 @@ from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
609
644
  from google.cloud.sql.connector import Connector
610
645
 
611
646
  async def init_connection_pool(connector: Connector) -> AsyncEngine:
612
- # initialize Connector object for connections to Cloud SQL
647
+ # creation function to generate asyncpg connections as 'async_creator' arg
613
648
  async def getconn() -> asyncpg.Connection:
614
- conn: asyncpg.Connection = await connector.connect_async(
615
- "project:region:instance", # Cloud SQL instance connection name
616
- "asyncpg",
617
- user="my-user",
618
- password="my-password",
619
- db="my-db-name"
620
- # ... additional database driver args
621
- )
622
- return conn
649
+ conn: asyncpg.Connection = await connector.connect_async(
650
+ "project:region:instance", # Cloud SQL instance connection name
651
+ "asyncpg",
652
+ user="my-user",
653
+ password="my-password",
654
+ db="my-db-name"
655
+ # ... additional database driver args
656
+ )
657
+ return conn
623
658
 
624
659
  # The Cloud SQL Python Connector can be used along with SQLAlchemy using the
625
660
  # 'async_creator' argument to 'create_async_engine'
@@ -1,3 +1,38 @@
1
+ Metadata-Version: 2.1
2
+ Name: cloud-sql-python-connector
3
+ Version: 1.14.0
4
+ Summary: The Cloud SQL Python Connector is a library that can be used alongside a database driver to allow users with sufficient permissions to connect to a Cloud SQL database without having to manually allowlist IPs or manage SSL certificates.
5
+ Home-page: https://github.com/GoogleCloudPlatform/cloud-sql-python-connector
6
+ Author: Google LLC
7
+ Author-email: googleapis-packages@google.com
8
+ License: Apache 2.0
9
+ Platform: Posix; MacOS X; Windows
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: Apache Software License
13
+ Classifier: Programming Language :: Python
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: aiofiles
23
+ Requires-Dist: aiohttp
24
+ Requires-Dist: cryptography>=42.0.0
25
+ Requires-Dist: Requests
26
+ Requires-Dist: google-auth>=2.28.0
27
+ Provides-Extra: pymysql
28
+ Requires-Dist: PyMySQL>=1.1.0; extra == "pymysql"
29
+ Provides-Extra: pg8000
30
+ Requires-Dist: pg8000>=1.31.1; extra == "pg8000"
31
+ Provides-Extra: pytds
32
+ Requires-Dist: python-tds>=1.15.0; extra == "pytds"
33
+ Provides-Extra: asyncpg
34
+ Requires-Dist: asyncpg>=0.30.0; extra == "asyncpg"
35
+
1
36
  <p align="center">
2
37
  <a href="https://cloud.google.com/blog/topics/developers-practitioners/how-connect-cloud-sql-using-python-easy-way">
3
38
  <img src="https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-sql-python-connector/main/docs/images/cloud-sql-python-connector.png" alt="cloud-sql-python-connector image">
@@ -502,6 +537,42 @@ The `create_async_connector` allows all the same input arguments as the
502
537
  Once a `Connector` object is returned by `create_async_connector` you can call
503
538
  its `connect_async` method, just as you would the `connect` method:
504
539
 
540
+ #### Asyncpg Connection Pool
541
+
542
+ ```python
543
+ import asyncpg
544
+ from google.cloud.sql.connector import Connector, create_async_connector
545
+
546
+ async def main():
547
+ # initialize Connector object for connections to Cloud SQL
548
+ connector = create_async_connector()
549
+
550
+ # creation function to generate asyncpg connections as the 'connect' arg
551
+ async def getconn(instance_connection_name, **kwargs) -> asyncpg.Connection:
552
+ return await connector.connect_async(
553
+ instance_connection_name,
554
+ "asyncpg",
555
+ user="my-user",
556
+ password="my-password",
557
+ db="my-db",
558
+ **kwargs, # ... additional asyncpg args
559
+ )
560
+
561
+ # initialize connection pool
562
+ pool = await asyncpg.create_pool(
563
+ "my-project:my-region:my-instance", connect=getconn
564
+ )
565
+
566
+ # acquire connection and query Cloud SQL database
567
+ async with pool.acquire() as conn:
568
+ res = await conn.fetch("SELECT NOW()")
569
+
570
+ # close Connector
571
+ await connector.close_async()
572
+ ```
573
+
574
+ #### SQLAlchemy Async Engine
575
+
505
576
  ```python
506
577
  import asyncpg
507
578
 
@@ -511,7 +582,7 @@ from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
511
582
  from google.cloud.sql.connector import Connector, create_async_connector
512
583
 
513
584
  async def init_connection_pool(connector: Connector) -> AsyncEngine:
514
- # initialize Connector object for connections to Cloud SQL
585
+ # creation function to generate asyncpg connections as 'async_creator' arg
515
586
  async def getconn() -> asyncpg.Connection:
516
587
  conn: asyncpg.Connection = await connector.connect_async(
517
588
  "project:region:instance", # Cloud SQL instance connection name
@@ -564,6 +635,40 @@ calls to `connector.close_async()` to cleanup resources.
564
635
  > This alternative requires that the running event loop be
565
636
  > passed in as the `loop` argument to `Connector()`.
566
637
 
638
+ #### Asyncpg Connection Pool
639
+
640
+ ```python
641
+ import asyncpg
642
+ from google.cloud.sql.connector import Connector, create_async_connector
643
+
644
+ async def main():
645
+ # initialize Connector object for connections to Cloud SQL
646
+ loop = asyncio.get_running_loop()
647
+ async with Connector(loop=loop) as connector:
648
+
649
+ # creation function to generate asyncpg connections as the 'connect' arg
650
+ async def getconn(instance_connection_name, **kwargs) -> asyncpg.Connection:
651
+ return await connector.connect_async(
652
+ instance_connection_name,
653
+ "asyncpg",
654
+ user="my-user",
655
+ password="my-password",
656
+ db="my-db",
657
+ **kwargs, # ... additional asyncpg args
658
+ )
659
+
660
+ # create connection pool
661
+ pool = await asyncpg.create_pool(
662
+ "my-project:my-region:my-instance", connect=getconn
663
+ )
664
+
665
+ # acquire connection and query Cloud SQL database
666
+ async with pool.acquire() as conn:
667
+ res = await conn.fetch("SELECT NOW()")
668
+ ```
669
+
670
+ #### SQLAlchemy Async Engine
671
+
567
672
  ```python
568
673
  import asyncio
569
674
  import asyncpg
@@ -574,17 +679,17 @@ from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
574
679
  from google.cloud.sql.connector import Connector
575
680
 
576
681
  async def init_connection_pool(connector: Connector) -> AsyncEngine:
577
- # initialize Connector object for connections to Cloud SQL
682
+ # creation function to generate asyncpg connections as 'async_creator' arg
578
683
  async def getconn() -> asyncpg.Connection:
579
- conn: asyncpg.Connection = await connector.connect_async(
580
- "project:region:instance", # Cloud SQL instance connection name
581
- "asyncpg",
582
- user="my-user",
583
- password="my-password",
584
- db="my-db-name"
585
- # ... additional database driver args
586
- )
587
- return conn
684
+ conn: asyncpg.Connection = await connector.connect_async(
685
+ "project:region:instance", # Cloud SQL instance connection name
686
+ "asyncpg",
687
+ user="my-user",
688
+ password="my-password",
689
+ db="my-db-name"
690
+ # ... additional database driver args
691
+ )
692
+ return conn
588
693
 
589
694
  # The Cloud SQL Python Connector can be used along with SQLAlchemy using the
590
695
  # 'async_creator' argument to 'create_async_engine'
@@ -12,6 +12,7 @@ google/cloud/sql/connector/__init__.py
12
12
  google/cloud/sql/connector/asyncpg.py
13
13
  google/cloud/sql/connector/client.py
14
14
  google/cloud/sql/connector/connection_info.py
15
+ google/cloud/sql/connector/connection_name.py
15
16
  google/cloud/sql/connector/connector.py
16
17
  google/cloud/sql/connector/enums.py
17
18
  google/cloud/sql/connector/exceptions.py
@@ -5,7 +5,7 @@ Requests
5
5
  google-auth>=2.28.0
6
6
 
7
7
  [asyncpg]
8
- asyncpg>=0.29.0
8
+ asyncpg>=0.30.0
9
9
 
10
10
  [pg8000]
11
11
  pg8000>=1.31.1
@@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  """
16
+
16
17
  import ssl
17
18
  from typing import Any, TYPE_CHECKING
18
19
 
@@ -17,7 +17,7 @@ from __future__ import annotations
17
17
  import asyncio
18
18
  import datetime
19
19
  import logging
20
- from typing import Any, Dict, Optional, Tuple, TYPE_CHECKING
20
+ from typing import Any, Optional, TYPE_CHECKING
21
21
 
22
22
  import aiohttp
23
23
  from cryptography.hazmat.backends import default_backend
@@ -98,7 +98,7 @@ class CloudSQLClient:
98
98
  project: str,
99
99
  region: str,
100
100
  instance: str,
101
- ) -> Dict[str, Any]:
101
+ ) -> dict[str, Any]:
102
102
  """Requests metadata from the Cloud SQL Instance
103
103
  and returns a dictionary containing the IP addresses and certificate
104
104
  authority of the Cloud SQL Instance.
@@ -113,7 +113,7 @@ class CloudSQLClient:
113
113
  :type instance: str
114
114
  :param instance: A string representing the name of the instance.
115
115
 
116
- :rtype: Dict[str: Union[Dict, str]]
116
+ :rtype: dict[str: Union[dict, str]]
117
117
  :returns: Returns a dictionary containing a dictionary of all IP
118
118
  addresses and their type and a string representing the
119
119
  certificate authority.
@@ -161,7 +161,7 @@ class CloudSQLClient:
161
161
  instance: str,
162
162
  pub_key: str,
163
163
  enable_iam_auth: bool = False,
164
- ) -> Tuple[str, datetime.datetime]:
164
+ ) -> tuple[str, datetime.datetime]:
165
165
  """Asynchronously requests an ephemeral certificate from the Cloud SQL Instance.
166
166
 
167
167
  :type project: str
@@ -17,7 +17,7 @@ from __future__ import annotations
17
17
  from dataclasses import dataclass
18
18
  import logging
19
19
  import ssl
20
- from typing import Any, Dict, Optional, TYPE_CHECKING
20
+ from typing import Any, Optional, TYPE_CHECKING
21
21
 
22
22
  from aiofiles.tempfile import TemporaryDirectory
23
23
 
@@ -41,7 +41,7 @@ class ConnectionInfo:
41
41
  client_cert: str
42
42
  server_ca_cert: str
43
43
  private_key: bytes
44
- ip_addrs: Dict[str, Any]
44
+ ip_addrs: dict[str, Any]
45
45
  database_version: str
46
46
  expiration: datetime.datetime
47
47
  context: Optional[ssl.SSLContext] = None
@@ -0,0 +1,51 @@
1
+ # Copyright 2024 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from dataclasses import dataclass
16
+ import re
17
+
18
+ # Instance connection name is the format <PROJECT>:<REGION>:<INSTANCE_NAME>
19
+ # Additionally, we have to support legacy "domain-scoped" projects
20
+ # (e.g. "google.com:PROJECT")
21
+ CONN_NAME_REGEX = re.compile(("([^:]+(:[^:]+)?):([^:]+):([^:]+)"))
22
+
23
+
24
+ @dataclass
25
+ class ConnectionName:
26
+ """ConnectionName represents a Cloud SQL instance's "instance connection name".
27
+
28
+ Takes the format "<PROJECT>:<REGION>:<INSTANCE_NAME>".
29
+ """
30
+
31
+ project: str
32
+ region: str
33
+ instance_name: str
34
+
35
+ def __str__(self) -> str:
36
+ return f"{self.project}:{self.region}:{self.instance_name}"
37
+
38
+
39
+ def _parse_instance_connection_name(connection_name: str) -> ConnectionName:
40
+ if CONN_NAME_REGEX.fullmatch(connection_name) is None:
41
+ raise ValueError(
42
+ "Arg `instance_connection_string` must have "
43
+ "format: PROJECT:REGION:INSTANCE, "
44
+ f"got {connection_name}."
45
+ )
46
+ connection_name_split = CONN_NAME_REGEX.split(connection_name)
47
+ return ConnectionName(
48
+ connection_name_split[1],
49
+ connection_name_split[3],
50
+ connection_name_split[4],
51
+ )
@@ -21,7 +21,7 @@ from functools import partial
21
21
  import logging
22
22
  from threading import Thread
23
23
  from types import TracebackType
24
- from typing import Any, Dict, Optional, Type, Union
24
+ from typing import Any, Optional, Type, Union
25
25
 
26
26
  import google.auth
27
27
  from google.auth.credentials import Credentials
@@ -32,7 +32,6 @@ from google.cloud.sql.connector.client import CloudSQLClient
32
32
  from google.cloud.sql.connector.enums import DriverMapping
33
33
  from google.cloud.sql.connector.enums import IPTypes
34
34
  from google.cloud.sql.connector.enums import RefreshStrategy
35
- from google.cloud.sql.connector.exceptions import ConnectorLoopError
36
35
  from google.cloud.sql.connector.instance import RefreshAheadCache
37
36
  from google.cloud.sql.connector.lazy import LazyRefreshCache
38
37
  import google.cloud.sql.connector.pg8000 as pg8000
@@ -132,7 +131,11 @@ class Connector:
132
131
  asyncio.run_coroutine_threadsafe(generate_keys(), self._loop),
133
132
  loop=self._loop,
134
133
  )
135
- self._cache: Dict[str, Union[RefreshAheadCache, LazyRefreshCache]] = {}
134
+ # initialize dict to store caches, key is a tuple consisting of instance
135
+ # connection name string and enable_iam_auth boolean flag
136
+ self._cache: dict[
137
+ tuple[str, bool], Union[RefreshAheadCache, LazyRefreshCache]
138
+ ] = {}
136
139
  self._client: Optional[CloudSQLClient] = None
137
140
 
138
141
  # initialize credentials
@@ -208,27 +211,16 @@ class Connector:
208
211
 
209
212
  Returns:
210
213
  A DB-API connection to the specified Cloud SQL instance.
211
-
212
- Raises:
213
- ConnectorLoopError: Event loop for background refresh is running in
214
- current thread. Error instead of hanging indefinitely.
215
214
  """
216
- try:
217
- # check if event loop is running in current thread
218
- if self._loop == asyncio.get_running_loop():
219
- raise ConnectorLoopError(
220
- "Connector event loop is running in current thread!"
221
- "Event loop must be attached to a different thread to prevent blocking code!"
222
- )
223
- # asyncio.get_running_loop will throw RunTimeError if no running loop is present
224
- except RuntimeError:
225
- pass
226
215
 
227
- # if event loop is not in current thread, proceed with connection
228
- connect_task = asyncio.run_coroutine_threadsafe(
229
- self.connect_async(instance_connection_string, driver, **kwargs), self._loop
216
+ # connect runs sync database connections on background thread.
217
+ # Async database connections should call 'connect_async' directly to
218
+ # avoid hanging indefinitely.
219
+ connect_future = asyncio.run_coroutine_threadsafe(
220
+ self.connect_async(instance_connection_string, driver, **kwargs),
221
+ self._loop,
230
222
  )
231
- return connect_task.result()
223
+ return connect_future.result()
232
224
 
233
225
  async def connect_async(
234
226
  self, instance_connection_string: str, driver: str, **kwargs: Any
@@ -274,15 +266,8 @@ class Connector:
274
266
  driver=driver,
275
267
  )
276
268
  enable_iam_auth = kwargs.pop("enable_iam_auth", self._enable_iam_auth)
277
- if instance_connection_string in self._cache:
278
- cache = self._cache[instance_connection_string]
279
- if enable_iam_auth != cache._enable_iam_auth:
280
- raise ValueError(
281
- f"connect() called with 'enable_iam_auth={enable_iam_auth}', "
282
- f"but previously used 'enable_iam_auth={cache._enable_iam_auth}'. "
283
- "If you require both for your use case, please use a new "
284
- "connector.Connector object."
285
- )
269
+ if (instance_connection_string, enable_iam_auth) in self._cache:
270
+ cache = self._cache[(instance_connection_string, enable_iam_auth)]
286
271
  else:
287
272
  if self._refresh_strategy == RefreshStrategy.LAZY:
288
273
  logger.debug(
@@ -309,7 +294,7 @@ class Connector:
309
294
  logger.debug(
310
295
  f"['{instance_connection_string}']: Connection info added to cache"
311
296
  )
312
- self._cache[instance_connection_string] = cache
297
+ self._cache[(instance_connection_string, enable_iam_auth)] = cache
313
298
 
314
299
  connect_func = {
315
300
  "pymysql": pymysql.connect,
@@ -345,7 +330,7 @@ class Connector:
345
330
  except Exception:
346
331
  # with an error from Cloud SQL Admin API call or IP type, invalidate
347
332
  # the cache and re-raise the error
348
- await self._remove_cached(instance_connection_string)
333
+ await self._remove_cached(instance_connection_string, enable_iam_auth)
349
334
  raise
350
335
  logger.debug(
351
336
  f"['{instance_connection_string}']: Connecting to {ip_address}:3307"
@@ -382,7 +367,9 @@ class Connector:
382
367
  await cache.force_refresh()
383
368
  raise
384
369
 
385
- async def _remove_cached(self, instance_connection_string: str) -> None:
370
+ async def _remove_cached(
371
+ self, instance_connection_string: str, enable_iam_auth: bool
372
+ ) -> None:
386
373
  """Stops all background refreshes and deletes the connection
387
374
  info cache from the map of caches.
388
375
  """
@@ -390,7 +377,7 @@ class Connector:
390
377
  f"['{instance_connection_string}']: Removing connection info from cache"
391
378
  )
392
379
  # remove cache from stored caches and close it
393
- cache = self._cache.pop(instance_connection_string)
380
+ cache = self._cache.pop((instance_connection_string, enable_iam_auth))
394
381
  await cache.close()
395
382
 
396
383
  def __enter__(self) -> Any:
@@ -21,13 +21,12 @@ from datetime import datetime
21
21
  from datetime import timedelta
22
22
  from datetime import timezone
23
23
  import logging
24
- import re
25
- from typing import Tuple
26
24
 
27
25
  import aiohttp
28
26
 
29
27
  from google.cloud.sql.connector.client import CloudSQLClient
30
28
  from google.cloud.sql.connector.connection_info import ConnectionInfo
29
+ from google.cloud.sql.connector.connection_name import _parse_instance_connection_name
31
30
  from google.cloud.sql.connector.exceptions import RefreshNotValidError
32
31
  from google.cloud.sql.connector.rate_limiter import AsyncRateLimiter
33
32
  from google.cloud.sql.connector.refresh_utils import _is_valid
@@ -37,22 +36,6 @@ logger = logging.getLogger(name=__name__)
37
36
 
38
37
  APPLICATION_NAME = "cloud-sql-python-connector"
39
38
 
40
- # Instance connection name is the format <PROJECT>:<REGION>:<INSTANCE>
41
- # Additionally, we have to support legacy "domain-scoped" projects
42
- # (e.g. "google.com:PROJECT")
43
- CONN_NAME_REGEX = re.compile(("([^:]+(:[^:]+)?):([^:]+):([^:]+)"))
44
-
45
-
46
- def _parse_instance_connection_name(connection_name: str) -> Tuple[str, str, str]:
47
- if CONN_NAME_REGEX.fullmatch(connection_name) is None:
48
- raise ValueError(
49
- "Arg `instance_connection_string` must have "
50
- "format: PROJECT:REGION:INSTANCE, "
51
- f"got {connection_name}."
52
- )
53
- connection_name_split = CONN_NAME_REGEX.split(connection_name)
54
- return connection_name_split[1], connection_name_split[3], connection_name_split[4]
55
-
56
39
 
57
40
  class RefreshAheadCache:
58
41
  """Cache that refreshes connection info in the background prior to expiration.
@@ -82,10 +65,13 @@ class RefreshAheadCache:
82
65
  connections.
83
66
  """
84
67
  # validate and parse instance connection name
85
- self._project, self._region, self._instance = _parse_instance_connection_name(
86
- instance_connection_string
68
+ conn_name = _parse_instance_connection_name(instance_connection_string)
69
+ self._project, self._region, self._instance = (
70
+ conn_name.project,
71
+ conn_name.region,
72
+ conn_name.instance_name,
87
73
  )
88
- self._instance_connection_string = instance_connection_string
74
+ self._conn_name = conn_name
89
75
 
90
76
  self._enable_iam_auth = enable_iam_auth
91
77
  self._keys = keys
@@ -122,8 +108,7 @@ class RefreshAheadCache:
122
108
  """
123
109
  self._refresh_in_progress.set()
124
110
  logger.debug(
125
- f"['{self._instance_connection_string}']: Connection info refresh "
126
- "operation started"
111
+ f"['{self._conn_name}']: Connection info refresh " "operation started"
127
112
  )
128
113
 
129
114
  try:
@@ -136,17 +121,16 @@ class RefreshAheadCache:
136
121
  self._enable_iam_auth,
137
122
  )
138
123
  logger.debug(
139
- f"['{self._instance_connection_string}']: Connection info "
140
- "refresh operation complete"
124
+ f"['{self._conn_name}']: Connection info " "refresh operation complete"
141
125
  )
142
126
  logger.debug(
143
- f"['{self._instance_connection_string}']: Current certificate "
127
+ f"['{self._conn_name}']: Current certificate "
144
128
  f"expiration = {connection_info.expiration.isoformat()}"
145
129
  )
146
130
 
147
131
  except aiohttp.ClientResponseError as e:
148
132
  logger.debug(
149
- f"['{self._instance_connection_string}']: Connection info "
133
+ f"['{self._conn_name}']: Connection info "
150
134
  f"refresh operation failed: {str(e)}"
151
135
  )
152
136
  if e.status == 403:
@@ -155,7 +139,7 @@ class RefreshAheadCache:
155
139
 
156
140
  except Exception as e:
157
141
  logger.debug(
158
- f"['{self._instance_connection_string}']: Connection info "
142
+ f"['{self._conn_name}']: Connection info "
159
143
  f"refresh operation failed: {str(e)}"
160
144
  )
161
145
  raise
@@ -189,18 +173,17 @@ class RefreshAheadCache:
189
173
  # check that refresh is valid
190
174
  if not await _is_valid(refresh_task):
191
175
  raise RefreshNotValidError(
192
- f"['{self._instance_connection_string}']: Invalid refresh operation. Certficate appears to be expired."
176
+ f"['{self._conn_name}']: Invalid refresh operation. Certficate appears to be expired."
193
177
  )
194
178
  except asyncio.CancelledError:
195
179
  logger.debug(
196
- f"['{self._instance_connection_string}']: Scheduled refresh"
197
- " operation cancelled"
180
+ f"['{self._conn_name}']: Scheduled refresh" " operation cancelled"
198
181
  )
199
182
  raise
200
183
  # bad refresh attempt
201
184
  except Exception as e:
202
185
  logger.exception(
203
- f"['{self._instance_connection_string}']: "
186
+ f"['{self._conn_name}']: "
204
187
  "An error occurred while performing refresh. "
205
188
  "Scheduling another refresh attempt immediately",
206
189
  exc_info=e,
@@ -217,7 +200,7 @@ class RefreshAheadCache:
217
200
  # calculate refresh delay based on certificate expiration
218
201
  delay = _seconds_until_refresh(refresh_data.expiration)
219
202
  logger.debug(
220
- f"['{self._instance_connection_string}']: Connection info refresh"
203
+ f"['{self._conn_name}']: Connection info refresh"
221
204
  " operation scheduled for "
222
205
  f"{(datetime.now(timezone.utc) + timedelta(seconds=delay)).isoformat(timespec='seconds')} "
223
206
  f"(now + {timedelta(seconds=delay)})"
@@ -241,7 +224,7 @@ class RefreshAheadCache:
241
224
  graceful exit.
242
225
  """
243
226
  logger.debug(
244
- f"['{self._instance_connection_string}']: Canceling connection info "
227
+ f"['{self._conn_name}']: Canceling connection info "
245
228
  "refresh operation tasks"
246
229
  )
247
230
  self._current.cancel()
@@ -21,7 +21,7 @@ from typing import Optional
21
21
 
22
22
  from google.cloud.sql.connector.client import CloudSQLClient
23
23
  from google.cloud.sql.connector.connection_info import ConnectionInfo
24
- from google.cloud.sql.connector.instance import _parse_instance_connection_name
24
+ from google.cloud.sql.connector.connection_name import _parse_instance_connection_name
25
25
  from google.cloud.sql.connector.refresh_utils import _refresh_buffer
26
26
 
27
27
  logger = logging.getLogger(name=__name__)
@@ -56,10 +56,13 @@ class LazyRefreshCache:
56
56
  connections.
57
57
  """
58
58
  # validate and parse instance connection name
59
- self._project, self._region, self._instance = _parse_instance_connection_name(
60
- instance_connection_string
59
+ conn_name = _parse_instance_connection_name(instance_connection_string)
60
+ self._project, self._region, self._instance = (
61
+ conn_name.project,
62
+ conn_name.region,
63
+ conn_name.instance_name,
61
64
  )
62
- self._instance_connection_string = instance_connection_string
65
+ self._conn_name = conn_name
63
66
 
64
67
  self._enable_iam_auth = enable_iam_auth
65
68
  self._keys = keys
@@ -91,13 +94,12 @@ class LazyRefreshCache:
91
94
  < (self._cached.expiration - timedelta(seconds=_refresh_buffer))
92
95
  ):
93
96
  logger.debug(
94
- f"['{self._instance_connection_string}']: Connection info "
97
+ f"['{self._conn_name}']: Connection info "
95
98
  "is still valid, using cached info"
96
99
  )
97
100
  return self._cached
98
101
  logger.debug(
99
- f"['{self._instance_connection_string}']: Connection info "
100
- "refresh operation started"
102
+ f"['{self._conn_name}']: Connection info " "refresh operation started"
101
103
  )
102
104
  try:
103
105
  conn_info = await self._client.get_connection_info(
@@ -109,16 +111,16 @@ class LazyRefreshCache:
109
111
  )
110
112
  except Exception as e:
111
113
  logger.debug(
112
- f"['{self._instance_connection_string}']: Connection info "
114
+ f"['{self._conn_name}']: Connection info "
113
115
  f"refresh operation failed: {str(e)}"
114
116
  )
115
117
  raise
116
118
  logger.debug(
117
- f"['{self._instance_connection_string}']: Connection info "
119
+ f"['{self._conn_name}']: Connection info "
118
120
  "refresh operation completed successfully"
119
121
  )
120
122
  logger.debug(
121
- f"['{self._instance_connection_string}']: Current certificate "
123
+ f"['{self._conn_name}']: Current certificate "
122
124
  f"expiration = {str(conn_info.expiration)}"
123
125
  )
124
126
  self._cached = conn_info
@@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  """
16
+
16
17
  import socket
17
18
  import ssl
18
19
  from typing import Any, TYPE_CHECKING
@@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  """
16
+
16
17
  import socket
17
18
  import ssl
18
19
  from typing import Any, TYPE_CHECKING
@@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  """
16
+
16
17
  import platform
17
18
  import socket
18
19
  import ssl
@@ -21,7 +21,7 @@ import copy
21
21
  import datetime
22
22
  import logging
23
23
  import random
24
- from typing import Any, Callable, List
24
+ from typing import Any, Callable
25
25
 
26
26
  import aiohttp
27
27
  from google.auth.credentials import Credentials
@@ -77,7 +77,7 @@ async def _is_valid(task: asyncio.Task) -> bool:
77
77
 
78
78
  def _downscope_credentials(
79
79
  credentials: Credentials,
80
- scopes: List[str] = ["https://www.googleapis.com/auth/sqlservice.login"],
80
+ scopes: list[str] = ["https://www.googleapis.com/auth/sqlservice.login"],
81
81
  ) -> Credentials:
82
82
  """Generate a down-scoped credential.
83
83
 
@@ -85,7 +85,7 @@ def _downscope_credentials(
85
85
  :param credentials
86
86
  Credentials object used to generate down-scoped credentials.
87
87
 
88
- :type scopes: List[str]
88
+ :type scopes: list[str]
89
89
  :param scopes
90
90
  List of Google scopes to include in down-scoped credentials object.
91
91
 
@@ -14,15 +14,13 @@ See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  """
16
16
 
17
- from typing import Tuple
18
-
19
17
  import aiofiles
20
18
  from cryptography.hazmat.backends import default_backend
21
19
  from cryptography.hazmat.primitives import serialization
22
20
  from cryptography.hazmat.primitives.asymmetric import rsa
23
21
 
24
22
 
25
- async def generate_keys() -> Tuple[bytes, str]:
23
+ async def generate_keys() -> tuple[bytes, str]:
26
24
  """A helper function to generate the private and public keys.
27
25
 
28
26
  backend - The value specified is default_backend(). This is because the
@@ -61,7 +59,7 @@ async def generate_keys() -> Tuple[bytes, str]:
61
59
 
62
60
  async def write_to_file(
63
61
  dir_path: str, serverCaCert: str, ephemeralCert: str, priv_key: bytes
64
- ) -> Tuple[str, str, str]:
62
+ ) -> tuple[str, str, str]:
65
63
  """
66
64
  Helper function to write the serverCaCert, ephemeral certificate and
67
65
  private key to .pem files in a given directory
@@ -12,4 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- __version__ = "1.12.1"
15
+ __version__ = "1.14.0"
@@ -67,11 +67,11 @@ setuptools.setup(
67
67
  "Intended Audience :: Developers",
68
68
  "License :: OSI Approved :: Apache Software License",
69
69
  "Programming Language :: Python",
70
- "Programming Language :: Python :: 3.8",
71
70
  "Programming Language :: Python :: 3.9",
72
71
  "Programming Language :: Python :: 3.10",
73
72
  "Programming Language :: Python :: 3.11",
74
73
  "Programming Language :: Python :: 3.12",
74
+ "Programming Language :: Python :: 3.13",
75
75
  ],
76
76
  platforms="Posix; MacOS X; Windows",
77
77
  packages=packages,
@@ -80,9 +80,9 @@ setuptools.setup(
80
80
  "pymysql": ["PyMySQL>=1.1.0"],
81
81
  "pg8000": ["pg8000>=1.31.1"],
82
82
  "pytds": ["python-tds>=1.15.0"],
83
- "asyncpg": ["asyncpg>=0.29.0"],
83
+ "asyncpg": ["asyncpg>=0.30.0"],
84
84
  },
85
- python_requires=">=3.8",
85
+ python_requires=">=3.9",
86
86
  include_package_data=True,
87
87
  zip_safe=False,
88
88
  package_data={"google.cloud.sql.connector": ["py.typed"]},