Joel's answer is the simplest, but may be inefficient (it generates all possible combinations, then culls those that do not match the constraint).
Ned's is much more efficient, but you have to hand-encode all the loops and constraints.
If you have more than a few ranges, you may wish to look at the constraint
module:
import constraint
p = constraint.Problem()
p.addVariable('a', range(minA, maxA, aStep))
p.addVariable('b', range(minB, maxB, bStep))
p.addConstraint(lambda a,b: a >= b, ['a','b'])
abArray = [(sol['a'], sol['b']) for sol in p.getSolutionIter()]
Note that this is not a good example; as a functional constraint can only be evaluated once all basis variables are known, this is essentially equivalent to Joel's solution. The real power of this approach only becomes evident once you start using more variables and a variety of interacting constraints.