Skip to content

Commit 3888ac6

Browse files
authored
fixit: Clean up Python sample at cloud-sql/mysql/sqlalchemy (GoogleCloudPlatform#10014)
## Description Fixes b/280881076 Note: Before submitting a pull request, please open an issue for discussion if you are not associated with Google. ## Checklist - [ ] I have followed [Sample Guidelines from AUTHORING_GUIDE.MD](https://togithub.com/GoogleCloudPlatform/python-docs-samples/blob/main/AUTHORING_GUIDE.md) - [ ] README is updated to include [all relevant information](https://togithub.com/GoogleCloudPlatform/python-docs-samples/blob/main/AUTHORING_GUIDE.md#readme-file) - [ ] **Tests** pass: `nox -s py-3.9` (see [Test Environment Setup](https://togithub.com/GoogleCloudPlatform/python-docs-samples/blob/main/AUTHORING_GUIDE.md#test-environment-setup)) - [ ] **Lint** pass: `nox -s lint` (see [Test Environment Setup](https://togithub.com/GoogleCloudPlatform/python-docs-samples/blob/main/AUTHORING_GUIDE.md#test-environment-setup)) - [ ] These samples need a new **API enabled** in testing projects to pass (let us know which ones) - [ ] These samples need a new/updated **env vars** in testing projects set to pass (let us know which ones) - [ ] This sample adds a new sample directory, and I updated the [CODEOWNERS file](https://togithub.com/GoogleCloudPlatform/python-docs-samples/blob/main/.github/CODEOWNERS) with the codeowners for this sample - [ ] This sample adds a new **Product API**, and I updated the [Blunderbuss issue/PR auto-assigner](https://togithub.com/GoogleCloudPlatform/python-docs-samples/blob/main/.github/blunderbuss.yml) with the codeowners for this sample - [ ] Please **merge** this PR for me once it is approved
1 parent 9d8fd09 commit 3888ac6

6 files changed

Lines changed: 69 additions & 38 deletions

File tree

cloud-sql/mysql/sqlalchemy/app.py

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434

3535
def init_connection_pool() -> sqlalchemy.engine.base.Engine:
36+
"""Sets up connection pool for the app."""
3637
# use a TCP socket when INSTANCE_HOST (e.g. 127.0.0.1) is defined
3738
if os.environ.get("INSTANCE_HOST"):
3839
return connect_tcp_socket()
@@ -45,7 +46,11 @@ def init_connection_pool() -> sqlalchemy.engine.base.Engine:
4546
if os.environ.get("INSTANCE_CONNECTION_NAME"):
4647
# Either a DB_USER or a DB_IAM_USER should be defined. If both are
4748
# defined, DB_IAM_USER takes precedence.
48-
return connect_with_connector_auto_iam_authn() if os.environ.get("DB_IAM_USER") else connect_with_connector()
49+
return (
50+
connect_with_connector_auto_iam_authn()
51+
if os.environ.get("DB_IAM_USER")
52+
else connect_with_connector()
53+
)
4954

5055
raise ValueError(
5156
"Missing database connection type. Please define one of INSTANCE_HOST, INSTANCE_UNIX_SOCKET, or INSTANCE_CONNECTION_NAME"
@@ -54,12 +59,15 @@ def init_connection_pool() -> sqlalchemy.engine.base.Engine:
5459

5560
# create 'votes' table in database if it does not already exist
5661
def migrate_db(db: sqlalchemy.engine.base.Engine) -> None:
62+
"""Creates the `votes` table if it doesn't exist."""
5763
with db.connect() as conn:
58-
conn.execute(sqlalchemy.text(
59-
"CREATE TABLE IF NOT EXISTS votes "
60-
"( vote_id SERIAL NOT NULL, time_cast timestamp NOT NULL, "
61-
"candidate VARCHAR(6) NOT NULL, PRIMARY KEY (vote_id) );"
62-
))
64+
conn.execute(
65+
sqlalchemy.text(
66+
"CREATE TABLE IF NOT EXISTS votes "
67+
"( vote_id SERIAL NOT NULL, time_cast timestamp NOT NULL, "
68+
"candidate VARCHAR(6) NOT NULL, PRIMARY KEY (vote_id) );"
69+
)
70+
)
6371
conn.commit()
6472

6573

@@ -75,32 +83,45 @@ def migrate_db(db: sqlalchemy.engine.base.Engine) -> None:
7583
# as the function is loaded. This is primarily to help testing.
7684
@app.before_first_request
7785
def init_db() -> sqlalchemy.engine.base.Engine:
86+
"""Initiates connection to database and its' structure."""
7887
global db
7988
db = init_connection_pool()
8089
migrate_db(db)
8190

8291

8392
@app.route("/", methods=["GET"])
8493
def render_index() -> str:
94+
"""Serves the index page of the app."""
8595
context = get_index_context(db)
8696
return render_template("index.html", **context)
8797

8898

8999
@app.route("/votes", methods=["POST"])
90100
def cast_vote() -> Response:
91-
team = request.form['team']
101+
"""Processes a single vote from user."""
102+
team = request.form["team"]
92103
return save_vote(db, team)
93104

94105

95106
# get_index_context gets data required for rendering HTML application
96107
def get_index_context(db: sqlalchemy.engine.base.Engine) -> dict:
108+
"""Retrieves data from the database about the votes.
109+
110+
Args:
111+
db: Connection to the database.
112+
113+
Returns:
114+
A dictionary containing information about votes.
115+
"""
97116
votes = []
98117

99118
with db.connect() as conn:
100119
# Execute the query and fetch all results
101-
recent_votes = conn.execute(sqlalchemy.text(
102-
"SELECT candidate, time_cast FROM votes ORDER BY time_cast DESC LIMIT 5"
103-
)).fetchall()
120+
recent_votes = conn.execute(
121+
sqlalchemy.text(
122+
"SELECT candidate, time_cast FROM votes ORDER BY time_cast DESC LIMIT 5"
123+
)
124+
).fetchall()
104125
# Convert the results into a list of dicts representing votes
105126
for row in recent_votes:
106127
votes.append({"candidate": row[0], "time_cast": row[1]})
@@ -122,6 +143,15 @@ def get_index_context(db: sqlalchemy.engine.base.Engine) -> dict:
122143

123144
# save_vote saves a vote to the database that was retrieved from form data
124145
def save_vote(db: sqlalchemy.engine.base.Engine, team: str) -> Response:
146+
"""Saves a single vote into the database.
147+
148+
Args:
149+
db: Connection to the database.
150+
team: The identifier of a team the vote is casted on.
151+
152+
Returns:
153+
A HTTP response that can be sent to the client.
154+
"""
125155
time_cast = datetime.datetime.now(tz=datetime.timezone.utc)
126156
# Verify that the team is one of the allowed options
127157
if team != "TABS" and team != "SPACES":

cloud-sql/mysql/sqlalchemy/connect_connector.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ def connect_with_connector() -> sqlalchemy.engine.base.Engine:
3232
# Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
3333
# keep secrets safe.
3434

35-
instance_connection_name = os.environ["INSTANCE_CONNECTION_NAME"] # e.g. 'project:region:instance'
35+
instance_connection_name = os.environ[
36+
"INSTANCE_CONNECTION_NAME"
37+
] # e.g. 'project:region:instance'
3638
db_user = os.environ["DB_USER"] # e.g. 'my-db-user'
3739
db_pass = os.environ["DB_PASS"] # e.g. 'my-db-password'
3840
db_name = os.environ["DB_NAME"] # e.g. 'my-database'
@@ -57,18 +59,14 @@ def getconn() -> pymysql.connections.Connection:
5759
# [START_EXCLUDE]
5860
# Pool size is the maximum number of permanent connections to keep.
5961
pool_size=5,
60-
6162
# Temporarily exceeds the set pool_size if no connections are available.
6263
max_overflow=2,
63-
6464
# The total number of concurrent connections for your application will be
6565
# a total of pool_size and max_overflow.
66-
6766
# 'pool_timeout' is the maximum number of seconds to wait when retrieving a
6867
# new connection from the pool. After the specified amount of time, an
6968
# exception will be thrown.
7069
pool_timeout=30, # 30 seconds
71-
7270
# 'pool_recycle' is the maximum number of seconds a connection can persist.
7371
# Connections that live longer than the specified amount of time will be
7472
# re-established
@@ -77,4 +75,5 @@ def getconn() -> pymysql.connections.Connection:
7775
)
7876
return pool
7977

78+
8079
# [END cloud_sql_mysql_sqlalchemy_connect_connector]

cloud-sql/mysql/sqlalchemy/connect_connector_auto_iam_authn.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ def connect_with_connector_auto_iam_authn() -> sqlalchemy.engine.base.Engine:
3131
# secure - consider a more secure solution such as
3232
# Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
3333
# keep secrets safe.
34-
instance_connection_name = os.environ["INSTANCE_CONNECTION_NAME"] # e.g. 'project:region:instance'
34+
instance_connection_name = os.environ[
35+
"INSTANCE_CONNECTION_NAME"
36+
] # e.g. 'project:region:instance'
3537
db_iam_user = os.environ["DB_IAM_USER"] # e.g. 'service-account-name'
3638
db_name = os.environ["DB_NAME"] # e.g. 'my-database'
3739

@@ -59,18 +61,14 @@ def getconn() -> pymysql.connections.Connection:
5961
# [START_EXCLUDE]
6062
# Pool size is the maximum number of permanent connections to keep.
6163
pool_size=5,
62-
6364
# Temporarily exceeds the set pool_size if no connections are available.
6465
max_overflow=2,
65-
6666
# The total number of concurrent connections for your application will be
6767
# a total of pool_size and max_overflow.
68-
6968
# 'pool_timeout' is the maximum number of seconds to wait when retrieving a
7069
# new connection from the pool. After the specified amount of time, an
7170
# exception will be thrown.
7271
pool_timeout=30, # 30 seconds
73-
7472
# 'pool_recycle' is the maximum number of seconds a connection can persist.
7573
# Connections that live longer than the specified amount of time will be
7674
# re-established
@@ -79,4 +77,5 @@ def getconn() -> pymysql.connections.Connection:
7977
)
8078
return pool
8179

80+
8281
# [END cloud_sql_mysql_sqlalchemy_auto_iam_authn]

cloud-sql/mysql/sqlalchemy/connect_tcp.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@
2222

2323

2424
def connect_tcp_socket() -> sqlalchemy.engine.base.Engine:
25-
""" Initializes a TCP connection pool for a Cloud SQL instance of MySQL. """
25+
"""Initializes a TCP connection pool for a Cloud SQL instance of MySQL."""
2626
# Note: Saving credentials in environment variables is convenient, but not
2727
# secure - consider a more secure solution such as
2828
# Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
2929
# keep secrets safe.
30-
db_host = os.environ["INSTANCE_HOST"] # e.g. '127.0.0.1' ('172.17.0.1' if deployed to GAE Flex)
30+
db_host = os.environ[
31+
"INSTANCE_HOST"
32+
] # e.g. '127.0.0.1' ('172.17.0.1' if deployed to GAE Flex)
3133
db_user = os.environ["DB_USER"] # e.g. 'my-db-user'
3234
db_pass = os.environ["DB_PASS"] # e.g. 'my-db-password'
3335
db_name = os.environ["DB_NAME"] # e.g. 'my-database'
@@ -43,11 +45,7 @@ def connect_tcp_socket() -> sqlalchemy.engine.base.Engine:
4345
db_cert = os.environ["DB_CERT"] # e.g. '/path/to/my/client-cert.pem'
4446
db_key = os.environ["DB_KEY"] # e.g. '/path/to/my/client-key.pem'
4547

46-
ssl_args = {
47-
"ssl_ca": db_root_cert,
48-
"ssl_cert": db_cert,
49-
"ssl_key": db_key
50-
}
48+
ssl_args = {"ssl_ca": db_root_cert, "ssl_cert": db_cert, "ssl_key": db_key}
5149
connect_args = ssl_args
5250

5351
# [START cloud_sql_mysql_sqlalchemy_connect_tcp]
@@ -74,19 +72,16 @@ def connect_tcp_socket() -> sqlalchemy.engine.base.Engine:
7472
# The total number of concurrent connections for your application will be
7573
# a total of pool_size and max_overflow.
7674
# [END cloud_sql_mysql_sqlalchemy_limit]
77-
7875
# [START cloud_sql_mysql_sqlalchemy_backoff]
7976
# SQLAlchemy automatically uses delays between failed connection attempts,
8077
# but provides no arguments for configuration.
8178
# [END cloud_sql_mysql_sqlalchemy_backoff]
82-
8379
# [START cloud_sql_mysql_sqlalchemy_timeout]
8480
# 'pool_timeout' is the maximum number of seconds to wait when retrieving a
8581
# new connection from the pool. After the specified amount of time, an
8682
# exception will be thrown.
8783
pool_timeout=30, # 30 seconds
8884
# [END cloud_sql_mysql_sqlalchemy_timeout]
89-
9085
# [START cloud_sql_mysql_sqlalchemy_lifetime]
9186
# 'pool_recycle' is the maximum number of seconds a connection can persist.
9287
# Connections that live longer than the specified amount of time will be
@@ -97,6 +92,7 @@ def connect_tcp_socket() -> sqlalchemy.engine.base.Engine:
9792
)
9893
return pool
9994

95+
10096
# [END cloud_sql_mysql_sqlalchemy_connect_tcp_sslcerts]
10197
# [END cloud_sql_mysql_sqlalchemy_sslcerts]
10298
# [END cloud_sql_mysql_sqlalchemy_connect_tcp]

cloud-sql/mysql/sqlalchemy/connect_unix.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,17 @@
1919

2020

2121
def connect_unix_socket() -> sqlalchemy.engine.base.Engine:
22-
""" Initializes a Unix socket connection pool for a Cloud SQL instance of MySQL. """
22+
"""Initializes a Unix socket connection pool for a Cloud SQL instance of MySQL."""
2323
# Note: Saving credentials in environment variables is convenient, but not
2424
# secure - consider a more secure solution such as
2525
# Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
2626
# keep secrets safe.
2727
db_user = os.environ["DB_USER"] # e.g. 'my-database-user'
2828
db_pass = os.environ["DB_PASS"] # e.g. 'my-database-password'
2929
db_name = os.environ["DB_NAME"] # e.g. 'my-database'
30-
unix_socket_path = os.environ["INSTANCE_UNIX_SOCKET"] # e.g. '/cloudsql/project:region:instance'
30+
unix_socket_path = os.environ[
31+
"INSTANCE_UNIX_SOCKET"
32+
] # e.g. '/cloudsql/project:region:instance'
3133

3234
pool = sqlalchemy.create_engine(
3335
# Equivalent URL:
@@ -42,18 +44,14 @@ def connect_unix_socket() -> sqlalchemy.engine.base.Engine:
4244
# [START_EXCLUDE]
4345
# Pool size is the maximum number of permanent connections to keep.
4446
pool_size=5,
45-
4647
# Temporarily exceeds the set pool_size if no connections are available.
4748
max_overflow=2,
48-
4949
# The total number of concurrent connections for your application will be
5050
# a total of pool_size and max_overflow.
51-
5251
# 'pool_timeout' is the maximum number of seconds to wait when retrieving a
5352
# new connection from the pool. After the specified amount of time, an
5453
# exception will be thrown.
5554
pool_timeout=30, # 30 seconds
56-
5755
# 'pool_recycle' is the maximum number of seconds a connection can persist.
5856
# Connections that live longer than the specified amount of time will be
5957
# re-established
@@ -62,4 +60,5 @@ def connect_unix_socket() -> sqlalchemy.engine.base.Engine:
6260
)
6361
return pool
6462

63+
6564
# [END cloud_sql_mysql_sqlalchemy_connect_unix]

cloud-sql/mysql/sqlalchemy/main.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
from flask import render_template, Response
15+
from flask import render_template, Request, Response
1616

1717
import functions_framework
1818

@@ -27,7 +27,15 @@
2727

2828

2929
@functions_framework.http
30-
def votes(request):
30+
def votes(request: Request) -> Response:
31+
"""Handles HTTP requests to our application.
32+
33+
Args:
34+
request: a Flask request object.
35+
36+
Returns:
37+
Flask HTTP Response to the client.
38+
"""
3139
if request.method == "GET":
3240
context = get_index_context(db)
3341
return render_template("index.html", **context)

0 commit comments

Comments
 (0)