OK, you're looking for a complete transformation with nothing (no symbols) not accounted for. So this is an all or nothing inquiry. I would do

```
muls = factor.atoms(Mul)
if muls:
  _, u = next(ordered(muls)).as_independent(x, y)  # ordered gives us the smallest Mul first
  xpart, ypart = u.as_independent(y)
  if ypart == y and xpart.as_base_exp()[0] == x:
    test = factor.subs(u, t)
    free = test.free_symbols
    if len(free) == 1 and free.pop() == t:
        print test


>>> factor=2*x*y/(3*x*y+cos(x*y))
>>> muls = factor.atoms(Mul)
>>> if muls:
...   _, u = next(ordered(muls)).as_independent(x, y)
...   xpart, ypart = u.as_independent(y)
...   if ypart == y and xpart.as_base_exp()[0] == x:
...     test = factor.subs(u, t)
...     free = test.free_symbols
...     if len(free) == 1 and free.pop() == t:
...         print test
... 
2*t/(3*t + cos(t))
```

I think what you have is essentially the same but it doesn't capture the all or nothing nature of the search I would modify what you have to be

```
    try:
        xpow = next(a for a in factor.atoms(Pow) if a.base == x)
        test = factor.subs(xpow, t/f(x))
        etc...
    except StopIteration:
        pass
```