Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Defeatist Elitist
Jun 17, 2012

I've got a carbon fixation.

credburn posted:

I think where I'm most confused is that I want to at the same time check to see if the variable is an integer, and also if it is 0. If the value was 7, that can just be done with int(var), but because it's 0, it crashes. Or just does things I'm not intending.

Check my edit for what you mean by that:
edit: broadly I don't think this is the best way to construct what you are making, but I'm not sure exactly what the assignment calls for. Are you specifically supposed to raise your own exceptions? If so, is there a reason the expeditions aren't being raised in the function itself? When you say non-ints should throw a ValueError, do you mean anything that is not literally an int already (including string representations of an int, floats, etc)? Do you mean anything that can't be cast to an int? Or do you mean anything that is not equivalent to an int?

Adbot
ADBOT LOVES YOU

Falcon2001
Oct 10, 2004

Eat your hamburgers, Apollo.
Pillbug
https://www.freecodecamp.org/news/truthy-and-falsy-values-in-python/ This is probably a pretty decent coverage of 'truthiness' in Python; python allows you to test some things.

If you want to check if the variable is an integer, DefeatistElitist has the right idea.

Falcon2001
Oct 10, 2004

Eat your hamburgers, Apollo.
Pillbug
Some more details, now that I review your code:

- Input() returns a string, so you'll need to try and cast that to an integer to do division. You probably want to add a try/catch clause to do like
code:
int(user_num)
and see if that fails - if it does, you can have the user try again or just display a friendly error.

Think about the flow there as well. You want to do a few things - Validate that the inputted numbers are integers, then ensure that the second number is not zero, then you can safely divide and return the value. I can be more explicit but I'm assuming you don't just want a response with 'here's the answer'.

Some references.
https://docs.python.org/3/tutorial/inputoutput.html
https://docs.python.org/3/tutorial/errors.html
https://www.programiz.com/python-programming/type-conversion-and-casting

QuarkJets
Sep 8, 2008

If div_num is 0 then a divide by zero exception should arise naturally when you try to divide by it, so it's not necessary to raise the exception yourself. Likewise if a value is entered that cannot become an integer (such as a word) then that results in a ValueError. You don't need to raise these exceptions, just catch them

A float can be converted to an integer, so here is where the wording of the question becomes very important. Are you supposed to raise an exception if any non-integer is inputted? In that case this:

code:

if int(div_num) == False:
            div_num = str(div_num)
            raise ValueError()

does not do what you want. int() always just turns input into an integer or raises an exception. You could instead use the tried and true decimal test:

code:

if "." in div_num:
            raise ValueError

Basically, "does the string contain a decimal point?" This is the only case where you should raise an Exception yourself. This can be placed in a function, for reuse on each argument

Have you covered string formatting at all? String concatenation is very intuitive for new programmers but is basically the worst way to form a string. Use f-strings instead:

code:

print(f"the divisor was {user_num}.")

This lets you insert variables directly into strings, they're very easy to read and edit and highly performant, plus you don't have to convert variables to strings yourself. Making a few style tweaks:

code:
def return_quotient(user_num, div_num):
    return user_num // div_num

def check_input(input_string):
    if "." in input_string:
        raise ValueError 

if __name__ == '__main__':
    try:
        user_input = input()
        check_input(user_input)
        numerator = int(user_input)

        user_input = input() 
        check_input(user_input)
        denominator = int(user_input)

        quotient = return_quotient(numerator, denominator)
        print(quotient)
    except ValueError:
        print(f'Input Exception: invalid input for int(): "{user_input}" ')
    except ZeroDivisionError:
        print('Zero Division Exception: integer division or modulo by zero')

Defeatist Elitist
Jun 17, 2012

I've got a carbon fixation.

If you want to be really cute a construct like

code:

if not int(div_num):
    raise ZeroDivisionError

will throw a ZeroDivisionError if int(div_num) is 0 and a ValueError if div_num cannot be converted into an int, but this probably isn't the best way to construct the script as a whole.

idk

edit: yeah ^^ neither of the errors actually has to be raised manually, that's why I asked if the assignment specifically asks you to do it.

D34THROW
Jan 29, 2012

RETAIL RETAIL LISTEN TO ME BITCH ABOUT RETAIL
:rant:
Off chance, does anyone happen to know of a decent linear stock cutting library for Python? I'd love a rectangular stock cutting library as well but I know that's computationally more complex.

Reason I ask is because I'm essentially generating cutlists for certain things with my project, so I'd love to take it a step further and automate the cut optimization process - and we deal with at most two different stock sizes of any given material, typically one. Occasionally we have to cut foam as well, which comes out of a single size of sheet.


EDIT: In the past hour, I have come to the conclusion that I was really overcomplicating things.

I have a single class to handle the math behind each use case. Instead of having a single table for each type of potential report header (PolyRoofHeader, PanRoofHeader, StormPanelHeader, etc), I'm simplifying to a single ReportHeader table that stores the report type, and uses that to determine what classes to use.

As for the data, the calculation classes all extend a base Calculator() class that has functions to serialize the full object for storage/reproduction and to serialize the succinct data for reporting. I think I can store the line item data (on reports with line item data) by serializing it into a list of dicts (or a dict of dicts with the keys being the line numbers).

It's a lot better to me than trying to micromanage eight thousand relationships and templates to ensure the view is right, rather than simplifying the model to use a common view.

D34THROW fucked around with this message at 17:38 on Feb 18, 2022

abelwingnut
Dec 23, 2002


so i'm trying to get python running on my win10 machine. what's the best way? this windows subsystem for linux seems pretty nifty but it also feels like it could be a disaster. as of now, i'm only trying to do web stuff, but could see myself doing more machine learning things.

e: or do i even want to try? i guess i could pretty easily deploy a linux vm on azure and just run everything there. might be the best idea given this machine is also my personal.

abelwingnut fucked around with this message at 17:40 on Feb 18, 2022

Macichne Leainig
Jul 26, 2012

by VG
I just download the Python installer from python.org, and then I'm a JetBrains fanatic so Pycharm sets up virtualenvs and all that goodness for me.

There's also Python in the Microsoft Store, but eh, I'd rather just have a normal rear end C:\Python3X folder.

wolrah
May 8, 2006
what?
WSL1 was a completely custom NT kernel subsystem implementing Linux compatibility sort of like a kernel-level reverse WINE. This had some weird compatibility issues and performance quirks as a result, but at the same time meant that Linux applications were using the same interfaces as native applications wherever possible.

WSL2 is more or less standard Linux running on Hyper-V with some nice integration.

Neither one would concern me for Python dev work, you likely aren't getting deep enough for the differences to matter, but WSL2 especially should be effectively indistinguishable from running the same distro in a standard Hyper-V VM. It's a great choice for dev work.

All of my computers are set up to dual boot and I can honestly say I've booted in to Linux natively twice since WSL2 released. It's that good.

The March Hare
Oct 15, 2006

Je rêve d'un
Wayne's World 3
Buglord
Yeah, WSL2 is very solid though the graphical extension still has some significantly annoying interactions such as causing all of my windows to flicker in and out of focus at rapid speeds if I take a screenshot of anything. So if you're planning on doing GUI stuff, I'd bear that in mind. Otherwise it is fantastic.

Falcon2001
Oct 10, 2004

Eat your hamburgers, Apollo.
Pillbug

abelwingnut posted:

so i'm trying to get python running on my win10 machine. what's the best way? this windows subsystem for linux seems pretty nifty but it also feels like it could be a disaster. as of now, i'm only trying to do web stuff, but could see myself doing more machine learning things.

e: or do i even want to try? i guess i could pretty easily deploy a linux vm on azure and just run everything there. might be the best idea given this machine is also my personal.

Don't get it from the MSStore, for whatever goddamn reason it has some weirdness with PIP and installers and things, just get it from Python.org. Seriously, just get the installer and make sure to click the 'add to path' option and you're gold.

If you're just dicking around, you don't need to worry about pyenv or virtual environments or anything like that, until you get to the point where you're trying to pass code around to other people or package it/etc, and at that point you can always then sit down and do it.

WSL2 is rad, but it's also not really a problem for Python; Python and Linux go hand in hand because a lot of linux distros come with Python so it's sort of a 'known value' in the environment, but it works just fine on Windows.

VSCode has great support for Python btw, and does integrate into pyenv and other things when you get to that point.

abelwingnut
Dec 23, 2002


thanks for all of the replies!

i also discovered anaconda, which is intriguing. any thoughts on it versus the aforementioned options? seems great for data work, not so sure on dev work.

credburn
Jun 22, 2016
A tangled skein of bad opinions, the hottest takes, and the the world's most misinformed nonsense. Do not engage with me, it's useless, and better yet, put me on ignore.
Thanks for your help regarding my struggle with exception handling. Truthy and falsy concepts have only briefly been touched on in my curriculum so far.

This assignment doesn't require me to raise the exceptions; just catch them. I couldn't actually remember why I had raised them, so I started over and rewrote the code, much simpler and cleaner now.

The assignment is:

quote:

Write a program that reads integers user_num and div_num as input, and output the quotient (user_num divided by div_num). Use a try block to perform all the statements. Use an except block to catch any ZeroDivisionError and output an exception message. Use another except block to catch any ValueError caused by invalid input and output an exception message.

My new code just looks like:
code:
def get_quotient(user_num, div_num):
    quotient = user_num // div_num
    return quotient

if __name__ == '__main__':
    try:
        user_num = input()
        if isinstance(user_num, int) == False:
            num = user_num
        user_num = int(user_num)
        div_num = int(input())
        if isinstance(div_num, int) == False:
            num = div_num
        div_num = int(div_num)
        quotient = get_quotient(user_num, div_num)
        print(quotient)
    except ValueError:
        print('Input Exception: invalid literal for int() with base 10: '"'{}'"''.format(num))
    except ZeroDivisionError:
        print('Zero Division Exception: integer division or modulo by zero')
I put if statements to check if either inputs are integers, the reason being so that the ValueError exception can properly reference them if they are not. So I have the variable num there to take on the value of either user_num or div_num, if either should not be a variable.

But when I run the program, the variable num only references user_num, even if it is an integer. So if I run the program with a variable as the numerator but a non-variable as the denominator, I still get this error:

quote:

Input Exception: invalid literal for int() with base 10: '5'

Gosh I think this part must be actually really simple and obvious and something I'm just overlooking.

My aim here is, with the first input as 5, and the second input as, say, 'watermelon', I want the num variable to print 'watermelon'. Or whatever the non-integer input may be.

Coding is loving fun.

credburn fucked around with this message at 02:10 on Feb 19, 2022

Defeatist Elitist
Jun 17, 2012

I've got a carbon fixation.

credburn posted:


My new code just looks like:
code:
def get_quotient(user_num, div_num):
    quotient = user_num // div_num
    return quotient

if __name__ == '__main__':
    try:
        user_num = input()
        if isinstance(user_num, int) == False:
            num = user_num
        user_num = int(user_num)
        div_num = int(input())
        if isinstance(div_num, int) == False:
            num = div_num
        div_num = int(div_num)
        quotient = get_quotient(user_num, div_num)
        print(quotient)
    except ValueError:
        print('Input Exception: invalid literal for int() with base 10: '"'{}'"''.format(num))
    except ZeroDivisionError:
        print('Zero Division Exception: integer division or modulo by zero')
But when I run the program, the variable num only references user_num, even if it is an integer. So if I run the program with a variable as the numerator but a non-variable as the denominator, I still get this error:

Gosh I think this part must be actually really simple and obvious and something I'm just overlooking.


input() results in a string, so when you check if user_num is an instance of int, it never is. Thus you always set num to the string value user_num.

QuarkJets
Sep 8, 2008

abelwingnut posted:

thanks for all of the replies!

i also discovered anaconda, which is intriguing. any thoughts on it versus the aforementioned options? seems great for data work, not so sure on dev work.

Anaconda is 100% exclusively what I encounter in dev-land, I've even seen dockerfiles from Facebook that are literally "download miniconda, now conda install the following packages:". Conda environments have full support in every popular IDE, even Visual Studio has embraced them.

The right way to get started with anaconda these days is to download the mambaforge installer, which sets up a bare bones conda environment with mamba, conda, and pip baked in. From there you can do whatever you want: spin off a venv for a pure-python environment, or use mamba/conda commands to create conda environments (mamba is just an optimized version of conda, it resolves complicated environments much faster).

It's also what I use when I just want a simple python environment on my home PC, because it is extremely easy to set up. So that has my full endorsement

QuarkJets
Sep 8, 2008

credburn posted:

Thanks for your help regarding my struggle with exception handling. Truthy and falsy concepts have only briefly been touched on in my curriculum so far.

This assignment doesn't require me to raise the exceptions; just catch them. I couldn't actually remember why I had raised them, so I started over and rewrote the code, much simpler and cleaner now.

The assignment is:

My new code just looks like:
code:
def get_quotient(user_num, div_num):
    quotient = user_num // div_num
    return quotient

if __name__ == '__main__':
    try:
        user_num = input()
        if isinstance(user_num, int) == False:
            num = user_num
        user_num = int(user_num)
        div_num = int(input())
        if isinstance(div_num, int) == False:
            num = div_num
        div_num = int(div_num)
        quotient = get_quotient(user_num, div_num)
        print(quotient)
    except ValueError:
        print('Input Exception: invalid literal for int() with base 10: '"'{}'"''.format(num))
    except ZeroDivisionError:
        print('Zero Division Exception: integer division or modulo by zero')
I put if statements to check if either inputs are integers, the reason being so that the ValueError exception can properly reference them if they are not. So I have the variable num there to take on the value of either user_num or div_num, if either should not be a variable.

But when I run the program, the variable num only references user_num, even if it is an integer. So if I run the program with a variable as the numerator but a non-variable as the denominator, I still get this error:

Gosh I think this part must be actually really simple and obvious and something I'm just overlooking.

My aim here is, with the first input as 5, and the second input as, say, 'watermelon', I want the num variable to print 'watermelon'. Or whatever the non-integer input may be.

Coding is loving fun.

As Defeatist Elitist pointed out, input() always returns a string, never an int. Those isinstance() checks will always return False. But just as a point of :eng101:, rather than comparing things to True or False it's better to just use those bools directly: the same thing can be accomplished with if not isinstance(div_num, int):

Take a look at this block of code:
Python code:
        div_num = int(input())
        if isinstance(div_num, int) == False:
            num = div_num
        div_num = int(div_num)
In that first line, you're trying to convert the input() to an int immediately. So when this value is "watermelon" an exception is immediately getting raised; that second line isn't even reached. Probably just a small oversight while you've been changing things around; drop that first int() and it should work (printing 'watermeleon' when you input that as the second value)

I also want to talk about this string formatting a little:
Python code:
print('Input Exception: invalid literal for int() with base 10: '"'{}'"''.format(num))
You don't need all of those extra single quotes; you can embed double-quotes directly within single-quoted strings, they're just normal characters at that point. This should look good:
Python code:
print('Input Exception: invalid literal for int() with base 10: "{}"'.format(num))
And since we don't need those if statements, we can just set `num` directly to input().

user_num and div_num don't really make sense for variable names, seeing as they're both user inputs. It'd be more consistent to use "numerator" and "denominator" or "user_num" and "user_div"

Putting it all together:

code:
def get_quotient(user_num, div_num):
    quotient = user_num // div_num
    return quotient

if __name__ == '__main__':
    try:
        num = input()
        user_num = int(num)
        num = input()
        user_div = int(num)
        quotient = get_quotient(user_num, user_div)
        print(quotient)
    except ValueError:
        print('Input Exception: invalid literal for int() with base 10: "{}"'.format(num))
    except ZeroDivisionError:
        print('Zero Division Exception: integer division or modulo by zero')
I'm going to recommend f-strings again, because they're like curly-brace formatting but better, but I'm assuming they haven't been covered

Have you covered docstrings? That function hella needs a docstring but if you haven't covered those then :shrug:

QuarkJets fucked around with this message at 03:56 on Feb 19, 2022

D34THROW
Jan 29, 2012

RETAIL RETAIL LISTEN TO ME BITCH ABOUT RETAIL
:rant:
My entire loving codebase needs docstrings but I haven't touched them this project because nobody's ever gonna see my code but me :v:

QuarkJets
Sep 8, 2008

D34THROW posted:

My entire loving codebase needs docstrings but I haven't touched them this project because nobody's ever gonna see my code but me :v:

That's a bad reason

CarForumPoster
Jun 26, 2013

⚡POWER⚡

D34THROW posted:

My entire loving codebase needs docstrings but I haven't touched them this project because nobody's ever gonna see my code but me :v:

Data Graham
Dec 28, 2009

📈📊🍪😋



# dear me six months from now, DO NOT REMOVE THIS CODE IT IS IMPORTANT
# dear me one year from now, I MEAN IT, YOU ARE NOT SMART ENOUGH TO REFACTOR THIS LOAD BEARING poo poo

Falcon2001
Oct 10, 2004

Eat your hamburgers, Apollo.
Pillbug

QuarkJets posted:

That's a bad reason

God I cannot count the number of times I've just been blinking at something I know I wrote a week ago. I've started making it a habit to improve docstrings even if I'm not heavily modifying the function.

D34THROW
Jan 29, 2012

RETAIL RETAIL LISTEN TO ME BITCH ABOUT RETAIL
:rant:
Is there a good formatting guide for doscstrings? I recognize that its in RST but like...theres annotaters and poo poo that I dont understand. Flask doscstrings show up really nicely in VSCode, class names are monospaced, etc, but even looking at the actual Flask code I have a hard time understanding how to do what.

QuarkJets
Sep 8, 2008

D34THROW posted:

Is there a good formatting guide for doscstrings? I recognize that its in RST but like...theres annotaters and poo poo that I dont understand. Flask doscstrings show up really nicely in VSCode, class names are monospaced, etc, but even looking at the actual Flask code I have a hard time understanding how to do what.

Yeah this PEP has pretty good guidelines

https://www.python.org/dev/peps/pep-0257/

Falcon2001
Oct 10, 2004

Eat your hamburgers, Apollo.
Pillbug

QuarkJets posted:

Yeah this PEP has pretty good guidelines

https://www.python.org/dev/peps/pep-0257/

One thing I love about Python is how consistently people seem to adhere and agree to PEP8 and other style guide stuff. Like I dunno, I never noticed this when working on C#, although it's possible I was just new enough to not know it.

D34THROW
Jan 29, 2012

RETAIL RETAIL LISTEN TO ME BITCH ABOUT RETAIL
:rant:
Just discovered the walrus operator, this is fantastic :allears:

Python code:
if form.validate_on_submit():
        if (report_type := int(form.roof_type.data)) == 1:
            # Poly roof.
            roof = PolyRoof(
                form.width.data, form.projection.data, form.fascia_sides.data
            )
        elif (report_type := int(form.roof_type.data)) == 2:
            # Pan roof.
            roof = PanRoof(
                form.width.data, form.projection.data, form.fascia_sides.data
            )
Saves me the extraneous assignment bullshit.

Data Graham
Dec 28, 2009

📈📊🍪😋



Meanwhile we don't get the Elvis operator ?:

Instead boring old or

Macichne Leainig
Jul 26, 2012

by VG

Data Graham posted:

Meanwhile we don't get the Elvis operator ?:

Instead boring old or

I do a lot of TS/JS frontend and Python backend, and this drat operator is the one thing that always trips me up going from JavaScript to Python.

QuarkJets
Sep 8, 2008

D34THROW posted:

Just discovered the walrus operator, this is fantastic :allears:

Python code:
if form.validate_on_submit():
        if (report_type := int(form.roof_type.data)) == 1:
            # Poly roof.
            roof = PolyRoof(
                form.width.data, form.projection.data, form.fascia_sides.data
            )
        elif (report_type := int(form.roof_type.data)) == 2:
            # Pan roof.
            roof = PanRoof(
                form.width.data, form.projection.data, form.fascia_sides.data
            )
Saves me the extraneous assignment bullshit.

gently caress yeah walrus operators, but you don't seem to be using report_type anywhere? And what's with these .datas everywhere? And what's with these magic numbers, 1 and 2?

D34THROW
Jan 29, 2012

RETAIL RETAIL LISTEN TO ME BITCH ABOUT RETAIL
:rant:

QuarkJets posted:

gently caress yeah walrus operators, but you don't seem to be using report_type anywhere? And what's with these .datas everywhere? And what's with these magic numbers, 1 and 2?

I didn't post all of the code for that function.

The form.* things are WTForms inputs, the .data property extracts the input.

report_type is used when generating the ORM's Report() object to stick into the DB.

The 1 and 2 correspond to the values from the RadioButton inputs and are used to determine which kind of Roof object to use and also stored in the database as the report's report_type field.


Now, to my original point: I just spent three loving hours banging my head against a wall with a Flask url_for() function before realizing that A) I was importing the wrong module and therefore the wrong blueprint for @bp.route and B) that url_for() expects the view function, not the route name. :doh:

teen phone cutie
Jun 18, 2012

last year i rewrote something awful from scratch because i hate myself
I have a general code-quality question. In my app, I have a Docker setup with mysql and nginx running with gunicorn serving my Flask app. In my entrypoint file I try to create the database and tables if they don't already exist.

The catch with running the containers is that I have 3 gunicorn workers going, so my entrypoint file gets run 3 times and the tables and database attempt to be created multiple times (protected by a try/except since every attempt after the first will fail). The code looks like this:

Python code:
import os
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import inspect
from app.config import app, DevelopmentConfig, ProductionConfig
from sqlalchemy_utils import create_database, database_exists, get_tables

config = DevelopmentConfig if os.environ.get(
    'FLASK_ENV') == 'development' else ProductionConfig

"""Init app"""
db = SQLAlchemy(app)

from app.routes.auth import auth

app.register_blueprint(auth)

if not database_exists(config.URL):
    # The Docker image will create this table automatically, but this line is for those not running in Docker
    create_database(config.URL)

has_no_tables = not inspect(db.engine).get_table_names()

if has_no_tables:
    try:
        print('Attempting to create database tables...', flush=True)
        db.create_all()
    except:
        # gunicorn has 3 workers so this path gets executed twice
        print('Database tables could not be created. - please check to see if they already exist.', flush=True)

The docker-compose file is pretty run-of-the-mill

XML code:
services:
  mysql:
    image: mysql:8
    restart: always
    ports:
      - 3306:3306
    volumes:
      - my-db:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_PASSWORD}
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
      - MYSQL_USER=${MYSQL_USER}
      - MYSQL_DATABASE=${MYSQL_DB}
    healthcheck:
      test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
      timeout: 20s
      retries: 10
volumes:
  my-db:
Is there a better way to do this? The end result I'm going for is to have the user run docker-compose up, and the database and tables are ready to go

teen phone cutie fucked around with this message at 17:58 on Feb 23, 2022

Deffon
Mar 28, 2010

The docker-compose setup looks good.

It's probably a good idea to only catch the specific db exception type that is raised when you're unable to create the tables.

If you need to update the db schema over time (e.g. by adding new columns), and it's not reasonable to drop the table beforehand, you probably want a db migration tool. I haven't used it myself, but Alembic exists.

Db migration tools keeps track of what schema changes have been performed on a given db by storing metadata in their own tables.
It's not uncommon to run migrations on application startup, so the tool makes the processes synchronize (usually by trying to lock a metadata table) so that only one process runs migrations to the same db at the same time while the others have to wait.

D34THROW
Jan 29, 2012

RETAIL RETAIL LISTEN TO ME BITCH ABOUT RETAIL
:rant:
Got frustrated trying to make a page render nicely so I'm taking a breather for something more simple - or so I thought.

What I'm trying to do is take my report table, which has FKs into user, job_card, and report_type, all on the respective id fields obviously.

What I can't loving figure out is how to get SQLAlchemy to give me a query table that has meaningful values for job_card.id, report_type.id and user.id instead of the PK.

Or, to put it a different way, I want the query to return a table with job.job_number, report_type.name, and user.username based on the id values stored in the FKs of each report row.

What the gently caress am I missing here :bang:


EDIT: Never mind, SQL thread pointed me in the right direction!

D34THROW fucked around with this message at 17:13 on Feb 24, 2022

teen phone cutie
Jun 18, 2012

last year i rewrote something awful from scratch because i hate myself

Deffon posted:

If you need to update the db schema over time (e.g. by adding new columns), and it's not reasonable to drop the table beforehand, you probably want a db migration tool. I haven't used it myself, but Alembic exists.

Thanks!

If I'm doing migrations on startup in the code, is there any harm in running the migration X amount of times for X amount of workers?

Deffon
Mar 28, 2010

teen phone cutie posted:

Thanks!

If I'm doing migrations on startup in the code, is there any harm in running the migration X amount of times for X amount of workers?

It shouldn't be a problem. db migration tools were built to survive in the age of load balanced microservices - it's expected that there may be multiple instances running and starting up at the same time.
Only one instance is allowed to perform migrations at a time, and the tool records each migration that have been performed on a given db (inside the db itself), so that e.g. the same table is not created twice.

The common case is that no migrations have to run, which should be a pretty cheap SQL query. The worst case is that the db needs to be built from scratch, and then all of the other instances are left waiting until then. It's not as if they could do much before the db schema is initialized though.

QuarkJets
Sep 8, 2008

teen phone cutie posted:

I have a general code-quality question. In my app, I have a Docker setup with mysql and nginx running with gunicorn serving my Flask app. In my entrypoint file I try to create the database and tables if they don't already exist.

The catch with running the containers is that I have 3 gunicorn workers going, so my entrypoint file gets run 3 times and the tables and database attempt to be created multiple times (protected by a try/except since every attempt after the first will fail). The code looks like this:

Python code:
import os
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import inspect
from app.config import app, DevelopmentConfig, ProductionConfig
from sqlalchemy_utils import create_database, database_exists, get_tables

config = DevelopmentConfig if os.environ.get(
    'FLASK_ENV') == 'development' else ProductionConfig

"""Init app"""
db = SQLAlchemy(app)

from app.routes.auth import auth

app.register_blueprint(auth)

if not database_exists(config.URL):
    # The Docker image will create this table automatically, but this line is for those not running in Docker
    create_database(config.URL)

has_no_tables = not inspect(db.engine).get_table_names()

if has_no_tables:
    try:
        print('Attempting to create database tables...', flush=True)
        db.create_all()
    except:
        # gunicorn has 3 workers so this path gets executed twice
        print('Database tables could not be created. - please check to see if they already exist.', flush=True)

The docker-compose file is pretty run-of-the-mill

XML code:
services:
  mysql:
    image: mysql:8
    restart: always
    ports:
      - 3306:3306
    volumes:
      - my-db:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_PASSWORD}
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
      - MYSQL_USER=${MYSQL_USER}
      - MYSQL_DATABASE=${MYSQL_DB}
    healthcheck:
      test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
      timeout: 20s
      retries: 10
volumes:
  my-db:
Is there a better way to do this? The end result I'm going for is to have the user run docker-compose up, and the database and tables are ready to go

I don't know anything about gunicorn but in parallel computing land a common pattern is to have a driver process (master, worker 0, etc) doing do-once tasks. Even in CUDA it's pretty common to see a kernel that's like "all ten billion threads do this but first wait for thread 0 to finish this other important thing". Figure out how your workers self-identify

teen phone cutie
Jun 18, 2012

last year i rewrote something awful from scratch because i hate myself

QuarkJets posted:

I don't know anything about gunicorn but in parallel computing land a common pattern is to have a driver process (master, worker 0, etc) doing do-once tasks. Even in CUDA it's pretty common to see a kernel that's like "all ten billion threads do this but first wait for thread 0 to finish this other important thing". Figure out how your workers self-identify

Thanks for the suggestion. I'll look into this one.

I actually ended up running the migrations in the entrypoint script before gunicorn starts up which ended up working out pretty well. And then I mounted my migrations folder as a volume so I can generate the migrations in the Docker container and they'll sync back to my local machine when running gunicorn in hot-reload mode.

Mycroft Holmes
Mar 26, 2010

by Azathoth
I've run into a problem with my homework. I have to take the following list:

code:
[{'rank': 1, 'title': 'Pride and Prejudice', 'author': 'Jane Austen', 'year': 1813}, {'rank': 2, 'title': 'To Kill a Mockingbird', 'author': 'Harper Lee', 'year': 1960}, {'rank': 3, 'title': 'The Great Gatsby', 'author': 'F. Scott Fitzgerald', 'year': 1925}, {'rank': 4, 'title': 'One Hundred Years of Solitude', 'author': 'Gabriel Garcia Marquez', 'year': 1967}, {'rank': 5, 'title': 'In Cold Blood', 'author': 'Truman Capote', 'year': 1965}, {'rank': 6, 'title': 'Wide Sargasso Sea', 'author': 'Jean Rhys"', 'year': 1966}, {'rank': 7, 'title': 'Brave New World', 'author': 'Aldous Huxley', 'year': 1932}, {'rank': 8, 'title': 'I Capture The Castle', 'author': 'Dodie Smith', 'year': 1948}, {'rank': 9, 'title': 'Jane Eyre,', 'author': 'Charlotte Bronte', 'year': 1847}, {'rank': 10, 'title': 'Crime and Punishment', 'author': 'Fyodor Dostoevsky', 'year': 1866}]
and my instructions are:

quote:

5. Create a list named book_data - 5a. Each entry in the list should contain the values for keys = 'rank', 'title', 'author' and 'year' - 5b. Each of the values must be separated by a colon(':') - HINT: Use a FOR-loop to process each entry in books_list Example: 1:Pride and Prejudice:Jane Austen:1813

I have no idea how to do this. I have spent 20 minutes trying to use .split to do it, before realizing it won't work. Any suggestions?

ploots
Mar 19, 2010
How are you being passed the input? Is it a string, a class, something else?

Mycroft Holmes
Mar 26, 2010

by Azathoth

Electoral Surgery posted:

How are you being passed the input? Is it a string, a class, something else?

I am being given the list straight up. When I try to split key from data, the .split command only gives me the key, not the data.

Adbot
ADBOT LOVES YOU

ploots
Mar 19, 2010
What are the entries in the list? What do you get when you do this:
code:
type(the_input_list[0])
str.split() is useful when you have a string that contains a bunch of fields separated by a delimiter. If that were the case, the entries in your list would look something like "1,Pride and Predjudice,Jane Austen,1813".

It looks like your list contains things that are more complicated that simple strings, so you probably want to access their parts in a different way.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply