Remove DynamoDB2/RDS2 decorators (#5383)
This commit is contained in:
parent
3d913f8f15
commit
5d897cc7e1
@ -3,30 +3,8 @@ import sys
|
|||||||
from contextlib import ContextDecorator
|
from contextlib import ContextDecorator
|
||||||
|
|
||||||
|
|
||||||
def lazy_load(
|
def lazy_load(module_name, element, boto3_name=None, backend=None):
|
||||||
module_name,
|
|
||||||
element,
|
|
||||||
boto3_name=None,
|
|
||||||
backend=None,
|
|
||||||
warn_repurpose=False,
|
|
||||||
use_instead=None,
|
|
||||||
):
|
|
||||||
def f(*args, **kwargs):
|
def f(*args, **kwargs):
|
||||||
if warn_repurpose:
|
|
||||||
import warnings
|
|
||||||
|
|
||||||
warnings.warn(
|
|
||||||
f"Module {element} has been deprecated, and will be repurposed in a later release. "
|
|
||||||
"Please see https://github.com/spulec/moto/issues/4526 for more information."
|
|
||||||
)
|
|
||||||
if use_instead:
|
|
||||||
import warnings
|
|
||||||
|
|
||||||
used, recommended = use_instead
|
|
||||||
warnings.warn(
|
|
||||||
f"Module {used} has been deprecated, and will be removed in a later release. Please use {recommended} instead. "
|
|
||||||
"See https://github.com/spulec/moto/issues/4526 for more information."
|
|
||||||
)
|
|
||||||
module = importlib.import_module(module_name, "moto")
|
module = importlib.import_module(module_name, "moto")
|
||||||
return getattr(module, element)(*args, **kwargs)
|
return getattr(module, element)(*args, **kwargs)
|
||||||
|
|
||||||
@ -77,9 +55,6 @@ mock_dax = lazy_load(".dax", "mock_dax")
|
|||||||
mock_dms = lazy_load(".dms", "mock_dms")
|
mock_dms = lazy_load(".dms", "mock_dms")
|
||||||
mock_ds = lazy_load(".ds", "mock_ds")
|
mock_ds = lazy_load(".ds", "mock_ds")
|
||||||
mock_dynamodb = lazy_load(".dynamodb", "mock_dynamodb")
|
mock_dynamodb = lazy_load(".dynamodb", "mock_dynamodb")
|
||||||
mock_dynamodb2 = lazy_load(
|
|
||||||
".dynamodb", "mock_dynamodb", use_instead=("mock_dynamodb2", "mock_dynamodb")
|
|
||||||
)
|
|
||||||
mock_dynamodbstreams = lazy_load(".dynamodbstreams", "mock_dynamodbstreams")
|
mock_dynamodbstreams = lazy_load(".dynamodbstreams", "mock_dynamodbstreams")
|
||||||
mock_elasticbeanstalk = lazy_load(
|
mock_elasticbeanstalk = lazy_load(
|
||||||
".elasticbeanstalk", "mock_elasticbeanstalk", backend="eb_backends"
|
".elasticbeanstalk", "mock_elasticbeanstalk", backend="eb_backends"
|
||||||
@ -137,7 +112,6 @@ mock_polly = lazy_load(".polly", "mock_polly")
|
|||||||
mock_quicksight = lazy_load(".quicksight", "mock_quicksight")
|
mock_quicksight = lazy_load(".quicksight", "mock_quicksight")
|
||||||
mock_ram = lazy_load(".ram", "mock_ram")
|
mock_ram = lazy_load(".ram", "mock_ram")
|
||||||
mock_rds = lazy_load(".rds", "mock_rds")
|
mock_rds = lazy_load(".rds", "mock_rds")
|
||||||
mock_rds2 = lazy_load(".rds", "mock_rds", use_instead=("mock_rds2", "mock_rds"))
|
|
||||||
mock_redshift = lazy_load(".redshift", "mock_redshift")
|
mock_redshift = lazy_load(".redshift", "mock_redshift")
|
||||||
mock_redshiftdata = lazy_load(
|
mock_redshiftdata = lazy_load(
|
||||||
".redshiftdata", "mock_redshiftdata", boto3_name="redshift-data"
|
".redshiftdata", "mock_redshiftdata", boto3_name="redshift-data"
|
||||||
@ -190,11 +164,7 @@ class MockAll(ContextDecorator):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.mocks = []
|
self.mocks = []
|
||||||
for mock in dir(sys.modules["moto"]):
|
for mock in dir(sys.modules["moto"]):
|
||||||
if (
|
if mock.startswith("mock_") and not mock == ("mock_all"):
|
||||||
mock.startswith("mock_")
|
|
||||||
and not mock.endswith("_deprecated")
|
|
||||||
and not mock == ("mock_all")
|
|
||||||
):
|
|
||||||
self.mocks.append(globals()[mock]())
|
self.mocks.append(globals()[mock]())
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
|
@ -3,11 +3,7 @@ import moto
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
decorators = [
|
decorators = [d for d in dir(moto) if d.startswith("mock_") and not d == "mock_all"]
|
||||||
d
|
|
||||||
for d in dir(moto)
|
|
||||||
if d.startswith("mock_") and not d.endswith("_deprecated") and not d == "mock_all"
|
|
||||||
]
|
|
||||||
decorator_functions = [getattr(moto, f) for f in decorators]
|
decorator_functions = [getattr(moto, f) for f in decorators]
|
||||||
BACKENDS = {f.boto3_name: (f.name, f.backend) for f in decorator_functions}
|
BACKENDS = {f.boto3_name: (f.name, f.backend) for f in decorator_functions}
|
||||||
BACKENDS["dynamodb_v20111205"] = ("dynamodb_v20111205", "dynamodb_backends")
|
BACKENDS["dynamodb_v20111205"] = ("dynamodb_v20111205", "dynamodb_backends")
|
||||||
|
@ -270,7 +270,7 @@ class UpdateExpressionExecutor(object):
|
|||||||
|
|
||||||
def execute(self, node=None):
|
def execute(self, node=None):
|
||||||
"""
|
"""
|
||||||
As explained in moto.dynamodb2.parsing.expressions.NestableExpressionParserMixin._create_node the order of nodes
|
As explained in moto.dynamodb.parsing.expressions.NestableExpressionParserMixin._create_node the order of nodes
|
||||||
in the AST can be translated of the order of statements in the expression. As such we can start at the root node
|
in the AST can be translated of the order of statements in the expression. As such we can start at the root node
|
||||||
and process the nodes 1-by-1. If no specific execution for the node type is defined we can execute the children
|
and process the nodes 1-by-1. If no specific execution for the node type is defined we can execute the children
|
||||||
in order since it will be a container node that is expandable and left child will be first in the statement.
|
in order since it will be a container node that is expandable and left child will be first in the statement.
|
||||||
|
@ -116,7 +116,7 @@ class NestableExpressionParserMixin(object):
|
|||||||
|
|
||||||
self.target_clauses looks like: ( SET a=3 >> REMOVE b )
|
self.target_clauses looks like: ( SET a=3 >> REMOVE b )
|
||||||
Returns:
|
Returns:
|
||||||
moto.dynamodb2.ast_nodes.Node: Node of an AST representing the Expression as produced by the factory.
|
moto.dynamodb.ast_nodes.Node: Node of an AST representing the Expression as produced by the factory.
|
||||||
"""
|
"""
|
||||||
assert len(self.target_clauses) > 0, "No nodes for {cn}".format(
|
assert len(self.target_clauses) > 0, "No nodes for {cn}".format(
|
||||||
cn=self.__class__.__name__
|
cn=self.__class__.__name__
|
||||||
@ -151,7 +151,7 @@ class ExpressionParser(metaclass=abc.ABCMeta):
|
|||||||
Start parsing the token_list from token_pos for the factory type.
|
Start parsing the token_list from token_pos for the factory type.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
moto.dynamodb2.ast_nodes.Node: AST which is root node of resulting abstract syntax tree
|
moto.dynamodb.ast_nodes.Node: AST which is root node of resulting abstract syntax tree
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -164,7 +164,7 @@ class ExpressionParser(metaclass=abc.ABCMeta):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
token(moto.dynamodb2.tokens.Token):
|
token(moto.dynamodb.tokens.Token):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True if token is a possible start for entries processed by `cls`
|
bool: True if token is a possible start for entries processed by `cls`
|
||||||
@ -200,7 +200,7 @@ class ExpressionParser(metaclass=abc.ABCMeta):
|
|||||||
Get the next token to be processed
|
Get the next token to be processed
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
moto.dynamodb2.tokens.Token: or None if no more next token
|
moto.dynamodb.tokens.Token: or None if no more next token
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return self.token_list[self.token_pos]
|
return self.token_list[self.token_pos]
|
||||||
@ -413,7 +413,7 @@ class NestableBinExpressionParser(ExpressionParser):
|
|||||||
|
|
||||||
self.target_nodes looks like: ( a >> + >> :val >> - >> :val2 )
|
self.target_nodes looks like: ( a >> + >> :val >> - >> :val2 )
|
||||||
Returns:
|
Returns:
|
||||||
moto.dynamodb2.ast_nodes.Node: Node of an AST representing the Expression as produced by the factory.
|
moto.dynamodb.ast_nodes.Node: Node of an AST representing the Expression as produced by the factory.
|
||||||
"""
|
"""
|
||||||
if len(self.target_nodes) == 1:
|
if len(self.target_nodes) == 1:
|
||||||
return UpdateExpressionValue(children=[self.target_nodes.popleft()])
|
return UpdateExpressionValue(children=[self.target_nodes.popleft()])
|
||||||
|
@ -134,8 +134,8 @@ class DynamoHandler(BaseResponse):
|
|||||||
@property
|
@property
|
||||||
def dynamodb_backend(self):
|
def dynamodb_backend(self):
|
||||||
"""
|
"""
|
||||||
:return: DynamoDB2 Backend
|
:return: DynamoDB Backend
|
||||||
:rtype: moto.dynamodb2.models.DynamoDBBackend
|
:rtype: moto.dynamodb.models.DynamoDBBackend
|
||||||
"""
|
"""
|
||||||
return dynamodb_backends[self.current_account][self.region]
|
return dynamodb_backends[self.current_account][self.region]
|
||||||
|
|
||||||
|
@ -1,140 +0,0 @@
|
|||||||
import boto3
|
|
||||||
import sure # noqa # pylint: disable=unused-import
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from boto3.dynamodb.conditions import Key
|
|
||||||
from moto import mock_dynamodb2
|
|
||||||
|
|
||||||
|
|
||||||
def test_deprecation_warning():
|
|
||||||
with pytest.warns(None) as record:
|
|
||||||
mock_dynamodb2()
|
|
||||||
str(record[0].message).should.contain(
|
|
||||||
"Module mock_dynamodb2 has been deprecated, and will be removed in a later release"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
Copy some basics test from DynamoDB
|
|
||||||
Verify that the behaviour still works using the 'mock_dynamodb2' decorator
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
@mock_dynamodb2
|
|
||||||
def test_basic_projection_expression_using_get_item():
|
|
||||||
dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
|
|
||||||
|
|
||||||
# Create the DynamoDB table.
|
|
||||||
dynamodb.create_table(
|
|
||||||
TableName="users",
|
|
||||||
KeySchema=[
|
|
||||||
{"AttributeName": "forum_name", "KeyType": "HASH"},
|
|
||||||
{"AttributeName": "subject", "KeyType": "RANGE"},
|
|
||||||
],
|
|
||||||
AttributeDefinitions=[
|
|
||||||
{"AttributeName": "forum_name", "AttributeType": "S"},
|
|
||||||
{"AttributeName": "subject", "AttributeType": "S"},
|
|
||||||
],
|
|
||||||
ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
|
|
||||||
)
|
|
||||||
table = dynamodb.Table("users")
|
|
||||||
|
|
||||||
table.put_item(
|
|
||||||
Item={"forum_name": "the-key", "subject": "123", "body": "some test message"}
|
|
||||||
)
|
|
||||||
|
|
||||||
table.put_item(
|
|
||||||
Item={
|
|
||||||
"forum_name": "not-the-key",
|
|
||||||
"subject": "123",
|
|
||||||
"body": "some other test message",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
result = table.get_item(
|
|
||||||
Key={"forum_name": "the-key", "subject": "123"},
|
|
||||||
ProjectionExpression="body, subject",
|
|
||||||
)
|
|
||||||
|
|
||||||
result["Item"].should.be.equal({"subject": "123", "body": "some test message"})
|
|
||||||
|
|
||||||
# The projection expression should not remove data from storage
|
|
||||||
result = table.get_item(Key={"forum_name": "the-key", "subject": "123"})
|
|
||||||
|
|
||||||
result["Item"].should.be.equal(
|
|
||||||
{"forum_name": "the-key", "subject": "123", "body": "some test message"}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@mock_dynamodb2
|
|
||||||
def test_condition_expression_with_dot_in_attr_name():
|
|
||||||
dynamodb = boto3.resource("dynamodb", region_name="us-east-2")
|
|
||||||
table_name = "Test"
|
|
||||||
dynamodb.create_table(
|
|
||||||
TableName=table_name,
|
|
||||||
KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}],
|
|
||||||
AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}],
|
|
||||||
BillingMode="PAY_PER_REQUEST",
|
|
||||||
)
|
|
||||||
table = dynamodb.Table(table_name)
|
|
||||||
|
|
||||||
email_like_str = "test@foo.com"
|
|
||||||
record = {"id": "key-0", "first": {email_like_str: {"third": {"VALUE"}}}}
|
|
||||||
table.put_item(Item=record)
|
|
||||||
|
|
||||||
table.update_item(
|
|
||||||
Key={"id": "key-0"},
|
|
||||||
UpdateExpression="REMOVE #first.#second, #other",
|
|
||||||
ExpressionAttributeNames={
|
|
||||||
"#first": "first",
|
|
||||||
"#second": email_like_str,
|
|
||||||
"#third": "third",
|
|
||||||
"#other": "other",
|
|
||||||
},
|
|
||||||
ExpressionAttributeValues={":value": "VALUE", ":one": 1},
|
|
||||||
ConditionExpression="size(#first.#second.#third) = :one AND contains(#first.#second.#third, :value)",
|
|
||||||
ReturnValues="ALL_NEW",
|
|
||||||
)
|
|
||||||
|
|
||||||
item = table.get_item(Key={"id": "key-0"})["Item"]
|
|
||||||
item.should.equal({"id": "key-0", "first": {}})
|
|
||||||
|
|
||||||
|
|
||||||
@mock_dynamodb2
|
|
||||||
def test_query_filter_boto3():
|
|
||||||
table_schema = {
|
|
||||||
"KeySchema": [
|
|
||||||
{"AttributeName": "pk", "KeyType": "HASH"},
|
|
||||||
{"AttributeName": "sk", "KeyType": "RANGE"},
|
|
||||||
],
|
|
||||||
"AttributeDefinitions": [
|
|
||||||
{"AttributeName": "pk", "AttributeType": "S"},
|
|
||||||
{"AttributeName": "sk", "AttributeType": "S"},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
|
|
||||||
table = dynamodb.create_table(
|
|
||||||
TableName="test-table", BillingMode="PAY_PER_REQUEST", **table_schema
|
|
||||||
)
|
|
||||||
|
|
||||||
for i in range(0, 3):
|
|
||||||
table.put_item(Item={"pk": "pk", "sk": "sk-{}".format(i)})
|
|
||||||
|
|
||||||
res = table.query(KeyConditionExpression=Key("pk").eq("pk"))
|
|
||||||
res["Items"].should.have.length_of(3)
|
|
||||||
|
|
||||||
res = table.query(KeyConditionExpression=Key("pk").eq("pk") & Key("sk").lt("sk-1"))
|
|
||||||
res["Items"].should.have.length_of(1)
|
|
||||||
res["Items"].should.equal([{"pk": "pk", "sk": "sk-0"}])
|
|
||||||
|
|
||||||
res = table.query(KeyConditionExpression=Key("pk").eq("pk") & Key("sk").lte("sk-1"))
|
|
||||||
res["Items"].should.have.length_of(2)
|
|
||||||
res["Items"].should.equal([{"pk": "pk", "sk": "sk-0"}, {"pk": "pk", "sk": "sk-1"}])
|
|
||||||
|
|
||||||
res = table.query(KeyConditionExpression=Key("pk").eq("pk") & Key("sk").gt("sk-1"))
|
|
||||||
res["Items"].should.have.length_of(1)
|
|
||||||
res["Items"].should.equal([{"pk": "pk", "sk": "sk-2"}])
|
|
||||||
|
|
||||||
res = table.query(KeyConditionExpression=Key("pk").eq("pk") & Key("sk").gte("sk-1"))
|
|
||||||
res["Items"].should.have.length_of(2)
|
|
||||||
res["Items"].should.equal([{"pk": "pk", "sk": "sk-1"}, {"pk": "pk", "sk": "sk-2"}])
|
|
@ -1,80 +0,0 @@
|
|||||||
import boto3
|
|
||||||
import pytest
|
|
||||||
import sure # noqa # pylint: disable=unused-import
|
|
||||||
|
|
||||||
from moto import mock_rds2
|
|
||||||
from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
|
|
||||||
|
|
||||||
|
|
||||||
def test_deprecation_warning():
|
|
||||||
with pytest.warns(None) as record:
|
|
||||||
mock_rds2()
|
|
||||||
str(record[0].message).should.contain(
|
|
||||||
"Module mock_rds2 has been deprecated, and will be removed in a later release."
|
|
||||||
)
|
|
||||||
str(record[0].message).should.contain("Please use mock_rds instead.")
|
|
||||||
|
|
||||||
|
|
||||||
@mock_rds2
|
|
||||||
def test_create_db_cluster__verify_default_properties():
|
|
||||||
client = boto3.client("rds", region_name="eu-north-1")
|
|
||||||
|
|
||||||
resp = client.create_db_cluster(
|
|
||||||
DBClusterIdentifier="cluster-id",
|
|
||||||
Engine="aurora",
|
|
||||||
MasterUsername="root",
|
|
||||||
MasterUserPassword="hunter2_",
|
|
||||||
)
|
|
||||||
resp.should.have.key("DBCluster")
|
|
||||||
|
|
||||||
cluster = resp["DBCluster"]
|
|
||||||
|
|
||||||
cluster.shouldnt.have.key(
|
|
||||||
"DatabaseName"
|
|
||||||
) # This was not supplied, so should not be returned
|
|
||||||
|
|
||||||
cluster.should.have.key("AllocatedStorage").equal(1)
|
|
||||||
cluster.should.have.key("AvailabilityZones")
|
|
||||||
set(cluster["AvailabilityZones"]).should.equal(
|
|
||||||
{"eu-north-1a", "eu-north-1b", "eu-north-1c"}
|
|
||||||
)
|
|
||||||
cluster.should.have.key("BackupRetentionPeriod").equal(1)
|
|
||||||
cluster.should.have.key("DBClusterIdentifier").equal("cluster-id")
|
|
||||||
cluster.should.have.key("DBClusterParameterGroup").equal("default.aurora8.0")
|
|
||||||
cluster.should.have.key("DBSubnetGroup").equal("default")
|
|
||||||
cluster.should.have.key("Status").equal("creating")
|
|
||||||
cluster.should.have.key("Endpoint").match(
|
|
||||||
"cluster-id.cluster-[a-z0-9]{12}.eu-north-1.rds.amazonaws.com"
|
|
||||||
)
|
|
||||||
endpoint = cluster["Endpoint"]
|
|
||||||
expected_readonly = endpoint.replace(
|
|
||||||
"cluster-id.cluster-", "cluster-id.cluster-ro-"
|
|
||||||
)
|
|
||||||
cluster.should.have.key("ReaderEndpoint").equal(expected_readonly)
|
|
||||||
cluster.should.have.key("MultiAZ").equal(False)
|
|
||||||
cluster.should.have.key("Engine").equal("aurora")
|
|
||||||
cluster.should.have.key("EngineVersion").equal("5.6.mysql_aurora.1.22.5")
|
|
||||||
cluster.should.have.key("Port").equal(3306)
|
|
||||||
cluster.should.have.key("MasterUsername").equal("root")
|
|
||||||
cluster.should.have.key("PreferredBackupWindow").equal("01:37-02:07")
|
|
||||||
cluster.should.have.key("PreferredMaintenanceWindow").equal("wed:02:40-wed:03:10")
|
|
||||||
cluster.should.have.key("ReadReplicaIdentifiers").equal([])
|
|
||||||
cluster.should.have.key("DBClusterMembers").equal([])
|
|
||||||
cluster.should.have.key("VpcSecurityGroups")
|
|
||||||
cluster.should.have.key("HostedZoneId")
|
|
||||||
cluster.should.have.key("StorageEncrypted").equal(False)
|
|
||||||
cluster.should.have.key("DbClusterResourceId").match(r"cluster-[A-Z0-9]{26}")
|
|
||||||
cluster.should.have.key("DBClusterArn").equal(
|
|
||||||
f"arn:aws:rds:eu-north-1:{ACCOUNT_ID}:cluster:cluster-id"
|
|
||||||
)
|
|
||||||
cluster.should.have.key("AssociatedRoles").equal([])
|
|
||||||
cluster.should.have.key("IAMDatabaseAuthenticationEnabled").equal(False)
|
|
||||||
cluster.should.have.key("EngineMode").equal("provisioned")
|
|
||||||
cluster.should.have.key("DeletionProtection").equal(False)
|
|
||||||
cluster.should.have.key("HttpEndpointEnabled").equal(False)
|
|
||||||
cluster.should.have.key("CopyTagsToSnapshot").equal(False)
|
|
||||||
cluster.should.have.key("CrossAccountClone").equal(False)
|
|
||||||
cluster.should.have.key("DeletionProtection").equal(False)
|
|
||||||
cluster.should.have.key("DomainMemberships").equal([])
|
|
||||||
cluster.should.have.key("TagList").equal([])
|
|
||||||
cluster.should.have.key("ClusterCreateTime")
|
|
Loading…
Reference in New Issue
Block a user