1

I've spent a 3+ hours on this for 18 of the last 21 days. Please, someone, tell me what I'm misunderstanding!

TL;DR: My code is repeatedly sending the db charset as a string to PyMysql, while it expects an object with an attribute called "encoding"

Background

This is Python code running on a docker container. A second container houses the database. The database address is stored in a .env variable called ENGINE_URL:

ENGINE_URL=mysql+pymysql://root:@database/starfinder_development?charset=utf8

I'm firing off Alembic and Flask-Alembic commands using click commands in the CLI. All of the methods below are used in CLI commands.

Models / Database Setup (works)

from flask import Flask
flask_app = Flask(__name__)

db_engine = SQLAlchemy(flask_app)

from my_application import models

def create_database():
    db_engine.create_all()

At this point I can open up the database container and use the MySql CLI to see that all of my models have now been converted into tables with columns and relationships.

Attempt 1: Alembic

Create Revision Files with Alembic (works)

from alembic.config import Config

def main(): # fires prior to any CLI command
    filepath = os.path.join(os.path.dirname(__file__),
                            "alembic.ini")
    alembic_config = Config(file_=filepath)
    alembic_config.set_main_option("sqlalchemy.url",
                                   ENGINE_URL)
    alembic_config.set_main_option("script_location",
                                   SCRIPT_LOCATION)
    migrate_cli(obj={"alembic_config": alembic_config})

def revision(ctx, message):
    alembic_config = ctx.obj["alembic_config"]
    alembic_command.revision(alembic_config, message)

At this point I have a migration file the was created exactly as expected. Then I need to upgrade the database using that migration...

Running Migrations with Alembic (fails)

def upgrade(ctx, migration_revision):
    alembic_config = ctx.obj["alembic_config"]
    migration_revision = migration_revision.lower()
    _dispatch_alembic_cmd(alembic_config, "upgrade",
                          revision=migration_revision)

firing this off with cli_command upgrade head causes a failure, which I've included here at the bottom because it has an identical stack trace to my second attempt.

Attempt 2: Flask-Alembic

This attempt finds me completely rewriting my main and revision commands, but it doesn't get as far as using upgrade.

Create Revision Files with Flask-Alembic (fails)

def main(): # fires prior to any CLI command
    alembic_config = Alembic()
    alembic_config.init_app(flask_app)
    migrate_cli(obj={"alembic_config": alembic_config})

def revision(ctx, message):
    with flask_app.app_context():
        alembic_config = ctx.obj["alembic_config"]
        print(alembic_config.revision(message))

This results in an error that is identical to the error from my previous attempt.

The stack trace in both cases:

(Identical failure using alembic upgrade & flask-alembic revision)

File "/Users/MyUser/.pyenv/versions/3.6.2/envs/sf/lib/python3.6/site-packages/pymysql/connections.py", line 678, in __init__
self.encoding = charset_by_name(self.charset).encoding
AttributeError: 'NoneType' object has no attribute 'encoding'

In response, I went into the above file & added a print on L677, immediately prior to the error:

print(self.charset)

utf8

Note: If I modify my ENGINE_URL to use a different ?charset=xxx, that change is reflected here.

So now I'm stumped

PyMysql expects self.charset to have an attribute encoding, but self.charset is simply a string. How can I change this to behave as expected?

Help?

A valid answer would be an alternative process, though the "most correct" answer would be to help me resolve the charset/encoding problem.

My primary goal here is simply to get migrations working on my flask app.

Community
  • 1
  • 1
  • 1
    Your diagnosis is wrong. Your code is sending the charset as a string *as it should*, but for some reason [`Charsets.by_name`](https://github.com/PyMySQL/PyMySQL/blob/master/pymysql/charset.py#L40) does not find it and so returns None instead of a `Charset` instance. It is the returned instance that would have the property [`encoding`](https://github.com/PyMySQL/PyMySQL/blob/master/pymysql/charset.py#L19). – Ilja Everilä Apr 25 '18 at 05:22

0 Answers0