Tips and Tricks About Computers, Web Development, Linux, the Internet and the Like
Python
Generating CSS Based on Images, also SEO-Friendly
Feb 20th
I’ve typed out a lot of CSS that’s focused on using a background image to replace text. I’ve been using a cross-browser CSS trick that’s SEO-friendly, I’m not sure if it’s documented anywhere. I came upon the solution myself when I was searching for a clever way to do this years ago and it’s become the standard method for me. However, I dislike the tedious task of typing out the CSS and referencing the image pixels and at work, I don’t want my developers wasting that time either.
I figured it’d be best to come up with a simple solution, so I started coding something out in bash and it worked:
#!/bin/bash
# css.sh - generate common css and html
# Tyler Mulligan (z@interwebninja.com)
# Last Update: 02/16/2011
# MIT License
create_block_image() {
feh -lr ${1:-.} | awk '{ print $3" "$4" "$8 }' | sed '1d' | while read l; do N=$((N+1));
cbi_css $l
done
feh -lr ${1:-.} | awk '{ print $3" "$4" "$8 }' | sed '1d' | while read l; do N=$((N+1));
cbi_html_a $l
done
feh -lr ${1:-.} | awk '{ print $3" "$4" "$8 }' | sed '1d' | while read l; do N=$((N+1));
cbi_html_div $l
done
}
cbi_css() {
f=${3##*/}
echo "#${f%.*} {
display: block;
background: url('$3') no-repeat 0 0;
width: 0;
height: $2px;
padding-left: $1px;
overflow: hidden;
}"
}
cbi_html_a() {
f=${3##*/}
echo "<a href=\"#\" id=\"${f%.*}\" alt=\"$f\" title=\"$f\">${f%.*}</a>"
}
cbi_html_div() {
f=${3##*/}
echo "
<div id=\"${f%.*}\"></div>
"
}
s=$1; shift; case $s in
--image-block|--cbi|-i) create_block_image $@;;
*) help help;;
esacWhich ouputs something like:
z@zygon:~/scripts/css$ ./css.sh -i img/
#dumbtubes-fav {
display: block;
background: url('img/dumbtubes-fav.png') no-repeat 0 0;
width: 0;
height: 14px;
padding-left: 16px;
overflow: hidden;
}
#submit_new {
display: block;
background: url('img/submit_new.png') no-repeat 0 0;
width: 0;
height: 106px;
padding-left: 244px;
overflow: hidden;
}
#dumbtubes-logo {
display: block;
background: url('img/dumbtubes-logo.png') no-repeat 0 0;
width: 0;
height: 70px;
padding-left: 274px;
overflow: hidden;
}
<a href="#" id="dumbtubes-fav" alt="dumbtubes-fav.png" title="dumbtubes-fav.png">dumbtubes-fav</a>
<a href="#" id="submit_new" alt="submit_new.png" title="submit_new.png">submit_new</a>
<a href="#" id="dumbtubes-logo" alt="dumbtubes-logo.png" title="dumbtubes-logo.png">dumbtubes-logo</a>
<div id="dumbtubes-fav"></div>
<div id="submit_new"></div>
<div id="dumbtubes-logo"></div>
To explain the way this CSS works, it uses the padding-left as the actual width, setting the width 0 and then pushes whatever content you have out of view using overflow:hidden to hit it from view. This makes it easy for search engines, keeping your HTML clean and CSS simple.
The bash script relies of “feh” a lightweight image viewer for linux that outputs a list of images and their dimensions. It also generates some sample HTML to quickly drop in and modify.
This is not an elegant solution and I’m not happy with the fact that it relies on feh. I’ve been slowly getting into python and I was amazed at how fast I was able to recreate this script in Python.
#!/usr/bin/python
# css.py - generate common css and html
# Tyler Mulligan (z@interwebninja.com)
# Last Update: 02/17/2011
# MIT License
from PIL import Image
import sys
import os.path
CSS_FORMAT = """#%s {\n\
display: block;\n\
background: url('%s') no-repeat 0 0;\n\
height: %spx;\n\
width: 0;\n\
padding: %spx;\n\
overflow: hidden;\n\
}\n"""
HTML_A_FORMAT = """<a href="#" id="%s" alt="%s" title="%s">%s</a>\n"""
HTML_DIV_FORMAT = """
<div id="%s">%s</div>
\n"""
css=""
html_a=""
html_div=""
for tf in os.listdir(sys.argv[1]):
f = os.path.join(sys.argv[1],tf)
if os.path.isfile(f) == True :
img = Image.open(f)
fid = os.path.splitext(tf)[0]
(width, height) = img.size[0:2]
css += CSS_FORMAT % (fid,f,height,width)
html_a += HTML_A_FORMAT % (fid,f,fid,fid)
html_div += HTML_DIV_FORMAT % (fid,fid)
print css
print html_a
print html_div This is just the basic idea, I didn’t spend more than 15 minutes on this rewrite, it does what I need it to do so far. It was suggested by friends if I make it any bigger, to look into a templating engine, such as mako. I hope this script can be useful to someone :). PS, HTML_DIV_FORMAT should be on one line, not sure why my syntax highlighter is trying to put it on 3.
MySQL and Python – The Problems and [Some] Solutions
Oct 15th
Intro
I’m still relatively new to Python. I dabble once in a while. Usually when I catch myself writing something overly complicated in bash. I figured doing MySQL from a Python script would be easier and better implemented in Python. Well, I was in for a few surprises I’d like to share with others looking to integrate with MySQL in Python before you encounter the same problems I did.
Getting on with it
As a Python noob, it made sense to me to use the python-mysqldb module. It “works” but not well. Read through the [working] code below and see if you can find the problem and how I circumvented it.
#!/bin/python
# Tyler Mulligan (tyler@doknowevil.net)
# MySQL the WRONG way in Python
import MySQLdb
def connection_settings():
global DB_ADMIN, DB_ADMIN_PASSWORD, DB_NAME, DB_HOST, DB_USER, DB_PASSWORD
DB_ADMIN="root"
DB_ADMIN_PASSWORD="local"
DB_NAME="foobardb"
DB_HOST="localhost"
DB_USER="foobar"
DB_PASSWORD="local"
def run_admin_sql():
conn = MySQLdb.Connection(host=DB_HOST, user=DB_ADMIN, passwd=DB_ADMIN_PASSWORD)
cursor = conn.cursor()
#cursor.execute("DROP USER %s@%s", (DB_USER, DB_HOST))
#cursor.execute("DROP DATABASE %s" % DB_NAME)
cursor.execute("CREATE USER %s@%s IDENTIFIED BY %s", (DB_USER, DB_HOST, DB_PASSWORD))
cursor.execute("CREATE DATABASE %s" % DB_NAME)
cursor.execute("GRANT USAGE ON %s.* TO %s@%s IDENTIFIED BY '%s'" % (DB_NAME, DB_USER, DB_HOST, DB_PASSWORD))
cursor.execute("GRANT ALL PRIVILEGES ON *.* to %s@%s IDENTIFIED BY %s", (DB_USER, DB_HOST, DB_PASSWORD))
cursor.close()
conn.close()
conn = MySQLdb.Connection(db=DB_NAME, host=DB_HOST, user=DB_USER, passwd=DB_PASSWORD)
cursor = conn.cursor()
cat_table_sql = """CREATE TABLE cats
(
`id` int(3) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`age` int(2) NOT NULL,
`type` int(2) NOT NULL,
PRIMARY KEY (`id`)
)"""
cursor.execute(cat_table_sql)
cursor.execute("INSERT INTO cats (name, age, type) VALUES (%s, %s, %s)", ("fluffy", 5, 1))
cursor.execute("INSERT INTO cats (name, age, type) VALUES (%s, %s, %s)", ("meow meow", 6, 1))
cursor.execute("INSERT INTO cats (name, age, type) VALUES (%s, %s, %s)", ("purrfect", 3, 1))
cursor.execute("SELECT * FROM cats")
results = cursor.fetchall()
cursor.close()
conn.close()
return results
connection_settings()
print run_admin_sql()The problem is parameterization. The python-mysqldb module sucks at it. It’s glaringly obvious when you look at the follow snippet and how I worked around it.
cursor.execute("CREATE USER %s@%s IDENTIFIED BY %s", (DB_USER, DB_HOST, DB_PASSWORD)) # GOOD
cursor.execute("CREATE DATABASE %s" % DB_NAME) # BAD NEWS BEARS
cursor.execute("CREATE DATABASE %s", (DB_NAME)) # but the module won't support this
The difference, is that the first implementation uses the module’s parameterization to escape the variables that are passed to it, making it safe. The second is using Python’s string formatter which means I’d have to do all the escaping and sanitization prior, which is obviously more dangerous and annoying.
The MySQLdb docs bury this little note, “Parameter placeholders can only be used to insert column values. They can not be used for other parts of SQL, such as table names, statements, etc.”
You can implement it this way but you’ll be wasting your time. There are better modules out there to make up for this one’s shortcomings. OurSQL and SQLAlchemy were the two libraries recommended to me in #python on irc.freenode.org. I will make another post when I develop the same example above using one of these libraries.
New Python eBook, Much Better, Down and Dirty
Jul 1st
Some friends recommended a book that’s a quicker pace with some better programming practices. Learn Python The Hard way (or quick way :-P). It reads more like a walk-through tutorial / quick reference and gives you easily repeatable programming practices that will get you a stronger feel for the language. It’s probably best used in combination with the Python Documentation. Also, a fair warning, Dive Into Python has war declared against it.

