Multiple meshes not welded with pydata
You end up with a lot of doubles (ie verts in same location) from your logic of running thru faces.  Remember on the cube each vert is shared by 3 faces.  Hence you end up with 4 x 3 x 8 (96) verts for four cubes, instead of 4 x 8 (32) vertices.

If you ran the the remove doubles operator on your mesh it would "weld" it.

Here is another take on this, using the list comprehension methods here https://blender.stackexchange.com/a/54130/15543 and then extending and offsetting for each row, col with gap for the vertices coordinates list, and incrementing the face vert index by the number of verts in the "brush" object for each copy.

import bpy
from mathutils import Vector
context = bpy.context
cube = context.object
mesh = cube.data
rows, cols = 5, 7
scale_x, scale_y = 1, 1
#--Gap between each row / column
gapx, gapy = 0.2, 0.2
#--Width of each row / column   
widthx, widthy = cube.dimensions.x * scale_x, cube.dimensions.y * scale_y
verts = []
faces = []
lenverts = len(mesh.vertices)
# make the 
for i in range(rows * cols):
    row = i % rows
    col = i // rows 
    # vector math
    verts.extend([v.co + Vector((col * (widthx + gapx), row * (widthy + gapy), 0.0))
             for v in mesh.vertices])
    faces.extend([[v + i * lenverts for v in p.vertices] for p in mesh.polygons])

# new mesh
mymesh = bpy.data.meshes.new("mymesh")        
mymesh.from_pydata(vertices=verts, edges=[], faces=faces)
obj = bpy.data.objects.new("foobar", mymesh)
context.scene.objects.link(obj)


You may want to adjust the vector math depending on where you want the origin to be.