aws-advanced-python-wrapper 1.1.0__tar.gz → 1.1.1__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 (73) hide show
  1. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/PKG-INFO +4 -2
  2. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/README.md +3 -1
  3. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/aurora_connection_tracker_plugin.py +4 -5
  4. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/driver_dialect_codes.py +0 -1
  5. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/driver_info.py +1 -1
  6. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/host_list_provider.py +6 -3
  7. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/mysql_driver_dialect.py +12 -2
  8. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/sql_alchemy_connection_provider.py +4 -0
  9. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/rdsutils.py +129 -75
  10. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/pyproject.toml +1 -1
  11. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/CONTRIBUTING.md +0 -0
  12. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/LICENSE +0 -0
  13. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/__init__.py +0 -0
  14. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/aurora_initial_connection_strategy_plugin.py +0 -0
  15. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/aws_secrets_manager_plugin.py +0 -0
  16. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/connect_time_plugin.py +0 -0
  17. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/connection_provider.py +0 -0
  18. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/credentials_provider_factory.py +0 -0
  19. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/database_dialect.py +0 -0
  20. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/default_plugin.py +0 -0
  21. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/developer_plugin.py +0 -0
  22. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/driver_configuration_profiles.py +0 -0
  23. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/driver_dialect.py +0 -0
  24. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/driver_dialect_manager.py +0 -0
  25. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/errors.py +0 -0
  26. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/exception_handling.py +0 -0
  27. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/execute_time_plugin.py +0 -0
  28. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/failover_plugin.py +0 -0
  29. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/failover_result.py +0 -0
  30. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/fastest_response_strategy_plugin.py +0 -0
  31. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/federated_plugin.py +0 -0
  32. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/host_availability.py +0 -0
  33. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/host_monitoring_plugin.py +0 -0
  34. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/host_selector.py +0 -0
  35. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/hostinfo.py +0 -0
  36. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/iam_plugin.py +0 -0
  37. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/okta_plugin.py +0 -0
  38. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/pep249.py +0 -0
  39. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/pg_driver_dialect.py +0 -0
  40. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/plugin.py +0 -0
  41. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/plugin_service.py +0 -0
  42. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/read_write_splitting_plugin.py +0 -0
  43. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/reader_failover_handler.py +0 -0
  44. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/resources/aws_advanced_python_wrapper_messages.properties +0 -0
  45. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/sqlalchemy_driver_dialect.py +0 -0
  46. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/stale_dns_plugin.py +0 -0
  47. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/states/__init__.py +0 -0
  48. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/states/session_state.py +0 -0
  49. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/states/session_state_service.py +0 -0
  50. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/__init__.py +0 -0
  51. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/atomic.py +0 -0
  52. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/cache_map.py +0 -0
  53. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/concurrent.py +0 -0
  54. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/decorators.py +0 -0
  55. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/failover_mode.py +0 -0
  56. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/iam_utils.py +0 -0
  57. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/log.py +0 -0
  58. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/messages.py +0 -0
  59. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/mysql_exception_handler.py +0 -0
  60. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/notifications.py +0 -0
  61. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/pg_exception_handler.py +0 -0
  62. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/properties.py +0 -0
  63. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/rds_url_type.py +0 -0
  64. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/saml_utils.py +0 -0
  65. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/sliding_expiration_cache.py +0 -0
  66. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/telemetry/default_telemetry_factory.py +0 -0
  67. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/telemetry/null_telemetry.py +0 -0
  68. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/telemetry/open_telemetry.py +0 -0
  69. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/telemetry/telemetry.py +0 -0
  70. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/telemetry/xray_telemetry.py +0 -0
  71. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/utils/utils.py +0 -0
  72. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/wrapper.py +0 -0
  73. {aws_advanced_python_wrapper-1.1.0 → aws_advanced_python_wrapper-1.1.1}/aws_advanced_python_wrapper/writer_failover_handler.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: aws-advanced-python-wrapper
3
- Version: 1.1.0
3
+ Version: 1.1.1
4
4
  Summary: Amazon Web Services (AWS) Advanced Python Wrapper
5
5
  Home-page: https://github.com/awslabs/aws-advanced-python-wrapper
6
6
  License: Apache-2.0
@@ -257,12 +257,14 @@ This `aws-advanced-python-wrapper` is being tested against the following Communi
257
257
 
258
258
  | Database | Versions |
259
259
  |-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
260
- | MySQL | 8.3.0 |
260
+ | MySQL | 8.4.0 |
261
261
  | PostgreSQL | 16.2 |
262
262
  | Aurora MySQL | - LTS version, see [here](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.Updates.Versions.html#AuroraMySQL.Updates.LTS) for more details. <br><br> - Latest release, as shown on [this page](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraMySQLReleaseNotes/AuroraMySQL.Updates.30Updates.html). |
263
263
  | Aurora PostgreSQL | - LTS version, see [here](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.Updates.LTS.html) for more details. <br><br> - Latest release, as shown on [this page](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraPostgreSQLReleaseNotes/AuroraPostgreSQL.Updates.html).) |
264
264
 
265
265
  The `aws-advanced-python-wrapper` is compatible with MySQL 5.7 and MySQL 8.0 as per MySQL Connector/Python.
266
+ > [!WARNING]\
267
+ > Due to recent internal changes with the `v9.0.0` MySQL Connector/Python driver in regards to connection handling, the AWS Advanced Python Wrapper is not recommended for usage with `v9.0.0`. The AWS Advanced Python Wrapper will be updated in the future for `v9.0.0` compatibility with the community driver.
266
268
 
267
269
  ## License
268
270
 
@@ -222,12 +222,14 @@ This `aws-advanced-python-wrapper` is being tested against the following Communi
222
222
 
223
223
  | Database | Versions |
224
224
  |-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
225
- | MySQL | 8.3.0 |
225
+ | MySQL | 8.4.0 |
226
226
  | PostgreSQL | 16.2 |
227
227
  | Aurora MySQL | - LTS version, see [here](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.Updates.Versions.html#AuroraMySQL.Updates.LTS) for more details. <br><br> - Latest release, as shown on [this page](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraMySQLReleaseNotes/AuroraMySQL.Updates.30Updates.html). |
228
228
  | Aurora PostgreSQL | - LTS version, see [here](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.Updates.LTS.html) for more details. <br><br> - Latest release, as shown on [this page](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraPostgreSQLReleaseNotes/AuroraPostgreSQL.Updates.html).) |
229
229
 
230
230
  The `aws-advanced-python-wrapper` is compatible with MySQL 5.7 and MySQL 8.0 as per MySQL Connector/Python.
231
+ > [!WARNING]\
232
+ > Due to recent internal changes with the `v9.0.0` MySQL Connector/Python driver in regards to connection handling, the AWS Advanced Python Wrapper is not recommended for usage with `v9.0.0`. The AWS Advanced Python Wrapper will be updated in the future for `v9.0.0` compatibility with the community driver.
231
233
 
232
234
  ## License
233
235
 
@@ -50,13 +50,12 @@ class OpenedConnectionTracker:
50
50
  """
51
51
 
52
52
  aliases: FrozenSet[str] = host_info.as_aliases()
53
- host: str = host_info.as_alias()
54
53
 
55
- if self._rds_utils.is_rds_instance(host):
56
- self._track_connection(host, conn)
54
+ if self._rds_utils.is_rds_instance(host_info.host):
55
+ self._track_connection(host_info.as_alias(), conn)
57
56
  return
58
57
 
59
- instance_endpoint: Optional[str] = next((alias for alias in aliases if self._rds_utils.is_rds_instance(alias)),
58
+ instance_endpoint: Optional[str] = next((alias for alias in aliases if self._rds_utils.is_rds_instance(self._rds_utils.remove_port(alias))),
60
59
  None)
61
60
  if not instance_endpoint:
62
61
  logger.debug("OpenedConnectionTracker.UnableToPopulateOpenedConnectionSet")
@@ -82,7 +81,7 @@ class OpenedConnectionTracker:
82
81
  return
83
82
 
84
83
  for instance in host:
85
- if instance is not None and self._rds_utils.is_rds_instance(instance):
84
+ if instance is not None and self._rds_utils.is_rds_instance(self._rds_utils.remove_port(instance)):
86
85
  instance_endpoint = instance
87
86
  break
88
87
 
@@ -13,7 +13,6 @@
13
13
  # limitations under the License.
14
14
 
15
15
  class DriverDialectCodes:
16
- SQLALCHEMY = "sqlalchemy"
17
16
  PSYCOPG = "psycopg"
18
17
  MYSQL_CONNECTOR_PYTHON = "mysql-connector-python"
19
18
  GENERIC = "generic"
@@ -15,4 +15,4 @@
15
15
 
16
16
  class DriverInfo:
17
17
  DRIVER_NAME = "aws_advanced_python_wrapper"
18
- DRIVER_VERSION = "1.0.0-rc"
18
+ DRIVER_VERSION = "1.1.1"
@@ -199,6 +199,8 @@ class RdsHostListProvider(DynamicHostListProvider, HostListProvider):
199
199
  else:
200
200
  self._cluster_instance_template = HostInfo(
201
201
  host=self._rds_utils.get_rds_instance_host_pattern(self._initial_host_info.host),
202
+ host_id=self._initial_host_info.host_id,
203
+ port=self._initial_host_info.port,
202
204
  host_availability_strategy=host_availability_strategy)
203
205
  self._validate_host_pattern(self._cluster_instance_template.host)
204
206
 
@@ -216,14 +218,15 @@ class RdsHostListProvider(DynamicHostListProvider, HostListProvider):
216
218
  self._cluster_id = cluster_id_suggestion.cluster_id
217
219
  self._is_primary_cluster_id = cluster_id_suggestion.is_primary_cluster_id
218
220
  else:
219
- cluster_url = self._rds_utils.get_rds_cluster_host_url(self._initial_host_info.url)
221
+ cluster_url = self._rds_utils.get_rds_cluster_host_url(self._initial_host_info.host)
220
222
  if cluster_url is not None:
221
- self._cluster_id = cluster_url
223
+ self._cluster_id = f"{cluster_url}:{self._cluster_instance_template.port}" \
224
+ if self._cluster_instance_template.is_port_specified() else cluster_url
222
225
  self._is_primary_cluster_id = True
223
226
  self._is_primary_cluster_id_cache.put(self._cluster_id, True,
224
227
  self._suggested_cluster_id_refresh_ns)
225
228
 
226
- self._is_initialized = True
229
+ self._is_initialized = True
227
230
 
228
231
  def _validate_host_pattern(self, host: str):
229
232
  if not self._rds_utils.is_dns_pattern_valid(host):
@@ -131,8 +131,18 @@ class MySQLDriverDialect(DriverDialect):
131
131
 
132
132
  if isinstance(obj, CMySQLCursor):
133
133
  try:
134
- if isinstance(obj._cnx, CMySQLConnection) or isinstance(obj._cnx, MySQLConnection):
135
- return obj._cnx
134
+ conn = None
135
+
136
+ if hasattr(obj, '_cnx'):
137
+ conn = obj._cnx
138
+ elif hasattr(obj, '_connection'):
139
+ conn = obj._connection
140
+ if conn is None:
141
+ return None
142
+
143
+ if isinstance(conn, CMySQLConnection) or isinstance(conn, MySQLConnection):
144
+ return conn
145
+
136
146
  except ReferenceError:
137
147
  return None
138
148
 
@@ -57,10 +57,12 @@ class SqlAlchemyPooledConnectionProvider(ConnectionProvider, CanReleaseResources
57
57
  self,
58
58
  pool_configurator: Optional[Callable] = None,
59
59
  pool_mapping: Optional[Callable] = None,
60
+ accept_url_func: Optional[Callable] = None,
60
61
  pool_expiration_check_ns: int = -1,
61
62
  pool_cleanup_interval_ns: int = -1):
62
63
  self._pool_configurator = pool_configurator
63
64
  self._pool_mapping = pool_mapping
65
+ self._accept_url_func = accept_url_func
64
66
 
65
67
  if pool_expiration_check_ns > -1:
66
68
  SqlAlchemyPooledConnectionProvider._POOL_EXPIRATION_CHECK_NS = pool_expiration_check_ns
@@ -80,6 +82,8 @@ class SqlAlchemyPooledConnectionProvider(ConnectionProvider, CanReleaseResources
80
82
  return self._database_pools.keys()
81
83
 
82
84
  def accepts_host_info(self, host_info: HostInfo, props: Properties) -> bool:
85
+ if self._accept_url_func:
86
+ return self._accept_url_func(host_info, props)
83
87
  url_type = SqlAlchemyPooledConnectionProvider._rds_utils.identify_rds_type(host_info.host)
84
88
  return RdsUrlType.RDS_INSTANCE == url_type
85
89
 
@@ -12,8 +12,10 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from re import search, sub
16
- from typing import Optional
15
+ from __future__ import annotations
16
+
17
+ from re import Match, search, sub
18
+ from typing import Dict, Optional
17
19
 
18
20
  from aws_advanced_python_wrapper.utils.rds_url_type import RdsUrlType
19
21
 
@@ -58,78 +60,101 @@ class RdsUtils:
58
60
  Example: test-postgres-instance-1.123456789012.rds.cn-northwest-1.amazonaws.com.cn
59
61
  """
60
62
 
61
- AURORA_DNS_PATTERN = r"(?P<instance>.+)\." \
62
- r"(?P<dns>proxy-|cluster-|cluster-ro-|cluster-custom-)?" \
63
+ AURORA_DNS_PATTERN = r"^(?P<instance>.+)\." \
64
+ r"(?P<dns>proxy-|cluster-|cluster-ro-|cluster-custom-|limitless-)?" \
63
65
  r"(?P<domain>[a-zA-Z0-9]+\." \
64
- r"(?P<region>[a-zA-Z0-9\-]+)\.rds\.amazonaws\.com)(?!\.cn$)"
65
- AURORA_INSTANCE_PATTERN = r"(?P<instance>.+)\." \
66
+ r"(?P<region>[a-zA-Z0-9\-]+)\.rds\.amazonaws\.com)(?!\.cn)$"
67
+ AURORA_INSTANCE_PATTERN = r"^(?P<instance>.+)\." \
66
68
  r"(?P<domain>[a-zA-Z0-9]+\." \
67
- r"(?P<region>[a-zA-Z0-9\-]+)\.rds\.amazonaws\.com)(?!\.cn$)"
68
- AURORA_CLUSTER_PATTERN = r"(?P<instance>.+)\." \
69
+ r"(?P<region>[a-zA-Z0-9\-]+)\.rds\.amazonaws\.com)(?!\.cn)$"
70
+ AURORA_CLUSTER_PATTERN = r"^(?P<instance>.+)\." \
69
71
  r"(?P<dns>cluster-|cluster-ro-)+" \
70
72
  r"(?P<domain>[a-zA-Z0-9]+\." \
71
- r"(?P<region>[a-zA-Z0-9\-]+)\.rds\.amazonaws\.com)(?!\.cn$)"
72
- AURORA_CUSTOM_CLUSTER_PATTERN = r"(?P<instance>.+)\." \
73
+ r"(?P<region>[a-zA-Z0-9\-]+)\.rds\.amazonaws\.com)(?!\.cn)$"
74
+ AURORA_CUSTOM_CLUSTER_PATTERN = r"^(?P<instance>.+)\." \
73
75
  r"(?P<dns>cluster-custom-)+" \
74
76
  r"(?P<domain>[a-zA-Z0-9]+\." \
75
- r"(?P<region>[a-zA-Z0-9\-]+)\.rds\.amazonaws\.com)(?!\.cn$)"
76
- AURORA_PROXY_DNS_PATTERN = r"(?P<instance>.+)\." \
77
+ r"(?P<region>[a-zA-Z0-9\-]+)\.rds\.amazonaws\.com)(?!\.cn)$"
78
+ AURORA_PROXY_DNS_PATTERN = r"^(?P<instance>.+)\." \
77
79
  r"(?P<dns>proxy-)+" \
78
80
  r"(?P<domain>[a-zA-Z0-9]+\." \
79
- r"(?P<region>[a-zA-Z0-9\\-]+)\.rds\.amazonaws\.com)(?!\.cn$)"
80
- AURORA_CHINA_DNS_PATTERN = r"(?P<instance>.+)\." \
81
- r"(?P<dns>proxy-|cluster-|cluster-ro-|cluster-custom-)?" \
81
+ r"(?P<region>[a-zA-Z0-9\\-]+)\.rds\.amazonaws\.com)(?!\.cn)$"
82
+ AURORA_OLD_CHINA_DNS_PATTERN = r"^(?P<instance>.+)\." \
83
+ r"(?P<dns>proxy-|cluster-|cluster-ro-|cluster-custom-|limitless-)?" \
84
+ r"(?P<domain>[a-zA-Z0-9]+\." \
85
+ r"(?P<region>[a-zA-Z0-9\-]+)\.rds\.amazonaws\.com\.cn)$"
86
+ AURORA_CHINA_DNS_PATTERN = r"^(?P<instance>.+)\." \
87
+ r"(?P<dns>proxy-|cluster-|cluster-ro-|cluster-custom-|limitless-)?" \
82
88
  r"(?P<domain>[a-zA-Z0-9]+\." \
83
- r"(?P<region>[a-zA-Z0-9\-]+)\.rds\.amazonaws\.com\.cn)"
84
- AURORA_CHINA_INSTANCE_PATTERN = r"(?P<instance>.+)\." \
85
- r"(?P<domain>[a-zA-Z0-9]+\." \
86
- r"(?P<region>[a-zA-Z0-9\-]+)\.rds\.amazonaws\.com\.cn)"
87
- AURORA_CHINA_CLUSTER_PATTERN = r"(?P<instance>.+)\." \
89
+ r"rds\.(?P<region>[a-zA-Z0-9\-]+)\.amazonaws\.com\.cn)$"
90
+ AURORA_OLD_CHINA_CLUSTER_PATTERN = r"^(?P<instance>.+)\." \
91
+ r"(?P<dns>cluster-|cluster-ro-)+" \
92
+ r"(?P<domain>[a-zA-Z0-9]+\." \
93
+ r"(?P<region>[a-zA-Z0-9\-]+)\.rds\.amazonaws\.com\.cn)$"
94
+ AURORA_CHINA_CLUSTER_PATTERN = r"^(?P<instance>.+)\." \
88
95
  r"(?P<dns>cluster-|cluster-ro-)+" \
89
96
  r"(?P<domain>[a-zA-Z0-9]+\." \
90
- r"(?P<region>[a-zA-Z0-9\-]+)\.rds\.amazonaws\.com\.cn)"
91
- AURORA_CHINA_CUSTOM_CLUSTER_PATTERN = r"(?P<instance>.+)\." \
92
- r"(?P<dns>cluster-custom-)+" \
93
- r"(?P<domain>[a-zA-Z0-9]+\." \
94
- r"(?P<region>[a-zA-Z0-9\-]+)\.rds\.amazonaws\.com\.cn)"
95
- AURORA_CHINA_PROXY_DNS_PATTERN = r"(?P<instance>.+)\." \
96
- r"(?P<dns>proxy-)+" \
97
- r"(?P<domain>[a-zA-Z0-9]+\." \
98
- r"(?P<region>[a-zA-Z0-9\-])+\.rds\.amazonaws\.com\.cn)"
97
+ r"rds\.(?P<region>[a-zA-Z0-9\-]+)\.amazonaws\.com\.cn)$"
98
+ AURORA_GOV_DNS_PATTERN = r"^(?P<instance>.+)\." \
99
+ r"(?P<dns>proxy-|cluster-|cluster-ro-|cluster-custom-|limitless-)?" \
100
+ r"(?P<domain>[a-zA-Z0-9]+\.rds\.(?P<region>[a-zA-Z0-9\-]+)" \
101
+ r"\.(amazonaws\.com|c2s\.ic\.gov|sc2s\.sgov\.gov))$"
102
+ AURORA_GOV_CLUSTER_PATTERN = r"^(?P<instance>.+)\." \
103
+ r"(?P<dns>cluster-|cluster-ro-)+" \
104
+ r"(?P<domain>[a-zA-Z0-9]+\.rds\.(?P<region>[a-zA-Z0-9\-]+)" \
105
+ r"\.(amazonaws\.com|c2s\.ic\.gov|sc2s\.sgov\.gov))$"
106
+ ELB_PATTERN = r"^(?<instance>.+)\.elb\.((?<region>[a-zA-Z0-9\-]+)\.amazonaws\.com)$"
99
107
 
100
108
  IP_V4 = r"^(([1-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){1}" \
101
- r"(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){2}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"
102
- IP_V6 = r"^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}$"
103
- IP_V6_COMPRESSED = r"^(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)::(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)$"
109
+ r"(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){2}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])"
110
+ IP_V6 = r"^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}"
111
+ IP_V6_COMPRESSED = r"^(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)::(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)"
104
112
 
105
113
  DNS_GROUP = "dns"
106
114
  DOMAIN_GROUP = "domain"
107
115
  INSTANCE_GROUP = "instance"
108
116
  REGION_GROUP = "region"
109
117
 
118
+ CACHE_DNS_PATTERNS: Dict[str, Match[str]] = {}
119
+ CACHE_PATTERNS: Dict[str, str] = {}
120
+
110
121
  def is_rds_cluster_dns(self, host: str) -> bool:
111
- return self._contains(host, [self.AURORA_CLUSTER_PATTERN, self.AURORA_CHINA_CLUSTER_PATTERN])
122
+ dns_group = self._get_dns_group(host)
123
+ return dns_group is not None and dns_group.casefold() in ["cluster-", "cluster-ro-"]
112
124
 
113
125
  def is_rds_custom_cluster_dns(self, host: str) -> bool:
114
- return self._contains(host, [self.AURORA_CUSTOM_CLUSTER_PATTERN, self.AURORA_CHINA_CUSTOM_CLUSTER_PATTERN])
126
+ dns_group = self._get_dns_group(host)
127
+ return dns_group is not None and dns_group.casefold() == "cluster-custom-"
115
128
 
116
129
  def is_rds_dns(self, host: str) -> bool:
117
- return self._contains(host, [self.AURORA_DNS_PATTERN, self.AURORA_CHINA_DNS_PATTERN])
130
+ if not host or not host.strip():
131
+ return False
132
+
133
+ pattern = self._find(host, [RdsUtils.AURORA_DNS_PATTERN,
134
+ RdsUtils.AURORA_CHINA_DNS_PATTERN,
135
+ RdsUtils.AURORA_OLD_CHINA_DNS_PATTERN,
136
+ RdsUtils.AURORA_GOV_DNS_PATTERN])
137
+ group = self._get_regex_group(pattern, RdsUtils.DNS_GROUP)
138
+
139
+ if group:
140
+ RdsUtils.CACHE_PATTERNS[host] = group
141
+
142
+ return pattern is not None
118
143
 
119
144
  def is_rds_instance(self, host: str) -> bool:
120
- return (self._contains(host, [self.AURORA_INSTANCE_PATTERN, self.AURORA_CHINA_INSTANCE_PATTERN])
121
- and self.is_rds_dns(host))
145
+ return self._get_dns_group(host) is None and self.is_rds_dns(host)
122
146
 
123
147
  def is_rds_proxy_dns(self, host: str) -> bool:
124
- return self._contains(host, [self.AURORA_PROXY_DNS_PATTERN, self.AURORA_CHINA_PROXY_DNS_PATTERN])
148
+ dns_group = self._get_dns_group(host)
149
+ return dns_group is not None and dns_group.casefold() == "proxy-"
125
150
 
126
151
  def get_rds_instance_host_pattern(self, host: str) -> str:
127
152
  if not host or not host.strip():
128
153
  return "?"
129
154
 
130
- match = self._find(host, [self.AURORA_DNS_PATTERN, self.AURORA_CHINA_DNS_PATTERN])
155
+ match = self._get_group(host, RdsUtils.DOMAIN_GROUP)
131
156
  if match:
132
- return f"?.{match.group(self.DOMAIN_GROUP)}"
157
+ return f"?.{match}"
133
158
 
134
159
  return "?"
135
160
 
@@ -137,56 +162,54 @@ class RdsUtils:
137
162
  if not host or not host.strip():
138
163
  return None
139
164
 
140
- match = self._find(host, [self.AURORA_DNS_PATTERN, self.AURORA_CHINA_DNS_PATTERN])
141
- if match:
142
- return match.group(self.REGION_GROUP)
165
+ group = self._get_group(host, RdsUtils.REGION_GROUP)
166
+ if group:
167
+ return group
143
168
 
169
+ elb_matcher = search(RdsUtils.ELB_PATTERN, host)
170
+ if elb_matcher:
171
+ return elb_matcher.group(RdsUtils.REGION_GROUP)
144
172
  return None
145
173
 
146
174
  def is_writer_cluster_dns(self, host: str) -> bool:
147
- if not host or not host.strip():
148
- return False
149
-
150
- match = self._find(host, [self.AURORA_CLUSTER_PATTERN, self.AURORA_CHINA_CLUSTER_PATTERN])
151
- if match:
152
- return "cluster-".casefold() == match.group(self.DNS_GROUP).casefold()
153
-
154
- return False
175
+ dns_group = self._get_dns_group(host)
176
+ return dns_group is not None and dns_group.casefold() == "cluster-"
155
177
 
156
178
  def is_reader_cluster_dns(self, host: str) -> bool:
157
- match = self._find(host, [self.AURORA_CLUSTER_PATTERN, self.AURORA_CHINA_CLUSTER_PATTERN])
158
- if match:
159
- return "cluster-ro-".casefold() == match.group(self.DNS_GROUP).casefold()
160
-
161
- return False
179
+ dns_group = self._get_dns_group(host)
180
+ return dns_group is not None and dns_group.casefold() == "cluster-ro-"
162
181
 
163
182
  def get_rds_cluster_host_url(self, host: str):
164
183
  if not host or not host.strip():
165
184
  return None
166
185
 
167
- if search(self.AURORA_CLUSTER_PATTERN, host):
168
- return sub(self.AURORA_CLUSTER_PATTERN, r"\g<instance>.cluster-\g<domain>", host)
169
-
170
- if search(self.AURORA_CHINA_CLUSTER_PATTERN, host):
171
- return sub(self.AURORA_CHINA_CLUSTER_PATTERN, r"\g<instance>.cluster-\g<domain>", host)
186
+ for pattern in [RdsUtils.AURORA_DNS_PATTERN,
187
+ RdsUtils.AURORA_CHINA_DNS_PATTERN,
188
+ RdsUtils.AURORA_OLD_CHINA_DNS_PATTERN,
189
+ RdsUtils.AURORA_GOV_DNS_PATTERN]:
190
+ if m := search(pattern, host):
191
+ group = self._get_regex_group(m, RdsUtils.DNS_GROUP)
192
+ if group is not None:
193
+ return sub(pattern, r"\g<instance>.cluster-\g<domain>", host)
194
+ return None
172
195
 
173
196
  return None
174
197
 
175
198
  def get_instance_id(self, host: str) -> Optional[str]:
176
- if not host or not host.strip():
177
- return None
178
-
179
- match = self._find(host, [self.AURORA_INSTANCE_PATTERN, self.AURORA_CHINA_INSTANCE_PATTERN])
180
- if match:
181
- return match.group(self.INSTANCE_GROUP)
199
+ if self._get_dns_group(host) is None:
200
+ return self._get_group(host, self.INSTANCE_GROUP)
182
201
 
183
202
  return None
184
203
 
185
204
  def is_ipv4(self, host: str) -> bool:
186
- return self._contains(host, [self.IP_V4])
205
+ if host is None or not host.strip():
206
+ return False
207
+ return search(RdsUtils.IP_V4, host) is not None
187
208
 
188
209
  def is_ipv6(self, host: str) -> bool:
189
- return self._contains(host, [self.IP_V6, self.IP_V6_COMPRESSED])
210
+ if host is None or not host.strip():
211
+ return False
212
+ return search(RdsUtils.IP_V6_COMPRESSED, host) is not None or search(RdsUtils.IP_V6, host) is not None
190
213
 
191
214
  def is_dns_pattern_valid(self, host: str) -> bool:
192
215
  return "?" in host
@@ -210,17 +233,48 @@ class RdsUtils:
210
233
 
211
234
  return RdsUrlType.OTHER
212
235
 
213
- def _contains(self, host: str, patterns: list) -> bool:
214
- if not host or not host.strip():
215
- return False
216
-
217
- return len([pattern for pattern in patterns if search(pattern, host)]) > 0
218
-
219
236
  def _find(self, host: str, patterns: list):
220
237
  if not host or not host.strip():
221
238
  return None
222
239
 
223
240
  for pattern in patterns:
241
+ match = RdsUtils.CACHE_DNS_PATTERNS.get(host)
242
+ if match:
243
+ return match
244
+
224
245
  match = search(pattern, host)
225
246
  if match:
247
+ RdsUtils.CACHE_DNS_PATTERNS[host] = match
226
248
  return match
249
+
250
+ return None
251
+
252
+ def _get_regex_group(self, pattern: Match[str], group_name: str):
253
+ if pattern is None:
254
+ return None
255
+ return pattern.group(group_name)
256
+
257
+ def _get_group(self, host: str, group: str):
258
+ if not host or not host.strip():
259
+ return None
260
+
261
+ pattern = self._find(host, [RdsUtils.AURORA_DNS_PATTERN,
262
+ RdsUtils.AURORA_CHINA_DNS_PATTERN,
263
+ RdsUtils.AURORA_OLD_CHINA_DNS_PATTERN,
264
+ RdsUtils.AURORA_GOV_DNS_PATTERN])
265
+ return self._get_regex_group(pattern, group)
266
+
267
+ def _get_dns_group(self, host: str):
268
+ return self._get_group(host, RdsUtils.DNS_GROUP)
269
+
270
+ def remove_port(self, url: str):
271
+ if not url or not url.strip():
272
+ return None
273
+ if ":" in url:
274
+ return url.split(":")[0]
275
+ return url
276
+
277
+ @staticmethod
278
+ def clear_cache():
279
+ RdsUtils.CACHE_PATTERNS.clear()
280
+ RdsUtils.CACHE_DNS_PATTERNS.clear()
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "aws_advanced_python_wrapper"
3
- version = "1.1.0"
3
+ version = "1.1.1"
4
4
  description = "Amazon Web Services (AWS) Advanced Python Wrapper"
5
5
  authors = ["Amazon Web Services"]
6
6
  readme = "README.md"