Non-stochastic gradient descent involves making exactly one update per epoch. True stochastic gradient descent makes progress considerably faster because it makes one update per input sample. A common compromise is to split the data into batches and make one update per batch. The simple fact that you split the data up into small batches provides some level of stochasticity even if the small batches are identical from one epoch to the next. Random sampling would provide more stochasticity, but incurs the penalty of having to copy the data. Shuffling the batches is frequently used to add some more stochasticity without incurring the penalty inherent in coping the data.
Models need contiguous data in each batch because the operations they perform are designed to work with contiguous data since that allows the code to be significantly optimised. Matrix operations can be efficiently parallelised when you have contiguous batches of matrices to operate on.
There is no requirement to go through the data exactly once, and as far as I know, there is no statistical reason why it would be preferable to random sampling. In fact if you read the code you posted carefully, you will see that if n_examples is not exactly divisible by batch_size a few of the training samples are never used.
An epoch is usually defined as a single pass through the training data, but really it is just a fixed length of training that we use for evaluating training progress.