Testing all of the already proposed solutions, I find they either
- convert to and from strings, which is inefficient
- can't handle negative numbers
- can't handle arrays
- have some numerical errors.
Here's my attempt at a solution which should handle all of these things. (Edit 2020-03-18: added np.asarray as suggested by A. West.)
def signif(x, p):
    x = np.asarray(x)
    x_positive = np.where(np.isfinite(x) & (x != 0), np.abs(x), 10**(p-1))
    mags = 10 ** (p - 1 - np.floor(np.log10(x_positive)))
    return np.round(x * mags) / mags
Testing:
def scottgigante(x, p):
    x_positive = np.where(np.isfinite(x) & (x != 0), np.abs(x), 10**(p-1))
    mags = 10 ** (p - 1 - np.floor(np.log10(x_positive)))
    return np.round(x * mags) / mags
def awest(x,p):
    return float(f'%.{p-1}e'%x)
def denizb(x,p):
    return float(('%.' + str(p-1) + 'e') % x)
def autumn(x, p):
    return np.format_float_positional(x, precision=p, unique=False, fractional=False, trim='k')
def greg(x, p):
    return round(x, -int(np.floor(np.sign(x) * np.log10(abs(x)))) + p-1)
def user11336338(x, p):         
    xr = (np.floor(np.log10(np.abs(x)))).astype(int)
    xr=10.**xr*np.around(x/10.**xr,p-1)   
    return xr
def dmon(x, p):
    if np.all(np.isfinite(x)):
        eset = np.seterr(all='ignore')
        mags = 10.0**np.floor(np.log10(np.abs(x)))  # omag's
        x = np.around(x/mags,p-1)*mags             # round(val/omag)*omag
        np.seterr(**eset)
        x = np.where(np.isnan(x), 0.0, x)           # 0.0 -> nan -> 0.0
    return x
def seanlake(x, p):
    __logBase10of2 = 3.010299956639811952137388947244930267681898814621085413104274611e-1
    xsgn = np.sign(x)
    absx = xsgn * x
    mantissa, binaryExponent = np.frexp( absx )
    decimalExponent = __logBase10of2 * binaryExponent
    omag = np.floor(decimalExponent)
    mantissa *= 10.0**(decimalExponent - omag)
    if mantissa < 1.0:
        mantissa *= 10.0
        omag -= 1.0
    return xsgn * np.around( mantissa, decimals=p - 1 ) * 10.0**omag
solns = [scottgigante, awest, denizb, autumn, greg, user11336338, dmon, seanlake]
xs = [
    1.114, # positive, round down
    1.115, # positive, round up
    -1.114, # negative
    1.114e-30, # extremely small
    1.114e30, # extremely large
    0, # zero
    float('inf'), # infinite
    [1.114, 1.115e-30], # array input
]
p = 3
print('input:', xs)
for soln in solns:
    print(f'{soln.__name__}', end=': ')
    for x in xs:
        try:
            print(soln(x, p), end=', ')
        except Exception as e:
            print(type(e).__name__, end=', ')
    print()
Results:
input: [1.114, 1.115, -1.114, 1.114e-30, 1.114e+30, 0, inf, [1.114, 1.115e-30]]
scottgigante: 1.11, 1.12, -1.11, 1.11e-30, 1.11e+30, 0.0, inf, [1.11e+00 1.12e-30], 
awest: 1.11, 1.11, -1.11, 1.11e-30, 1.11e+30, 0.0, inf, TypeError, 
denizb: 1.11, 1.11, -1.11, 1.11e-30, 1.11e+30, 0.0, inf, TypeError, 
autumn: 1.11, 1.11, -1.11, 0.00000000000000000000000000000111, 1110000000000000000000000000000., 0.00, inf, TypeError, 
greg: 1.11, 1.11, -1.114, 1.11e-30, 1.11e+30, ValueError, OverflowError, TypeError, 
user11336338: 1.11, 1.12, -1.11, 1.1100000000000002e-30, 1.1100000000000001e+30, nan, nan, [1.11e+00 1.12e-30], 
dmon: 1.11, 1.12, -1.11, 1.1100000000000002e-30, 1.1100000000000001e+30, 0.0, inf, [1.11e+00 1.12e-30], 
seanlake: 1.11, 1.12, -1.11, 1.1100000000000002e-30, 1.1100000000000001e+30, 0.0, inf, ValueError, 
Timing: 
def test_soln(soln):
    try:
        soln(np.linspace(1, 100, 1000), 3)
    except Exception:
        [soln(x, 3) for x in np.linspace(1, 100, 1000)]
for soln in solns:
    print(soln.__name__)
    %timeit test_soln(soln)
Results:
scottgigante
135 µs ± 15.3 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
awest
2.23 ms ± 430 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
denizb
2.18 ms ± 352 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
autumn
2.92 ms ± 206 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
greg
14.1 ms ± 1.21 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
user11336338
157 µs ± 50.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
dmon
142 µs ± 8.52 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
seanlake
20.7 ms ± 994 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)