Skip to content

Commit a26f44b

Browse files
committed
Implement __fspath__ a bit more smart: return the wrapped if str/bytes and return wrapped if it doesn't have any __fspath__ method.
1 parent ce5ba16 commit a26f44b

File tree

5 files changed

+71
-3
lines changed

5 files changed

+71
-3
lines changed

src/lazy_object_proxy/cext.c

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,31 @@ static PyObject *Proxy_str(ProxyObject *self)
222222

223223
/* ------------------------------------------------------------------------- */
224224

225+
static PyObject *Proxy_fspath(ProxyObject *self)
226+
{
227+
_Py_IDENTIFIER(__fspath__);
228+
Proxy__ENSURE_WRAPPED_OR_RETURN_NULL(self);
229+
PyObject *func = NULL;
230+
PyObject *fspath = NULL;
231+
232+
if (PyUnicode_Check(self->wrapped) || PyBytes_Check(self->wrapped)) {
233+
Py_INCREF(self->wrapped);
234+
return self->wrapped;
235+
}
236+
237+
func = _PyObject_LookupSpecial(self->wrapped, &PyId___fspath__);
238+
if (NULL == func) {
239+
Py_INCREF(self->wrapped);
240+
return self->wrapped;
241+
}
242+
243+
fspath = PyObject_CallFunctionObjArgs(func, NULL);
244+
Py_DECREF(func);
245+
return fspath;
246+
}
247+
248+
/* ------------------------------------------------------------------------- */
249+
225250
static PyObject *Proxy_add(PyObject *o1, PyObject *o2)
226251
{
227252
Proxy__WRAPPED_REPLACE_OR_RETURN_NULL(o1);
@@ -1282,7 +1307,7 @@ static PyMethodDef Proxy_methods[] = {
12821307
{ "__reversed__", (PyCFunction)Proxy_reversed, METH_NOARGS, 0 },
12831308
{ "__reduce__", (PyCFunction)Proxy_reduce, METH_NOARGS, 0 },
12841309
{ "__reduce_ex__", (PyCFunction)Proxy_reduce, METH_O, 0 },
1285-
{ "__fspath__", (PyCFunction)Proxy_str, METH_NOARGS, 0 },
1310+
{ "__fspath__", (PyCFunction)Proxy_fspath, METH_NOARGS, 0 },
12861311
#if PY_MAJOR_VERSION >= 3
12871312
{ "__round__", (PyCFunction)Proxy_round, METH_NOARGS, 0 },
12881313
#endif

src/lazy_object_proxy/simple.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from .compat import with_metaclass
66
from .utils import cached_property
77
from .utils import identity
8+
from .utils import string_types
89

910

1011
def make_proxy_method(code):
@@ -100,7 +101,17 @@ def __repr__(self, __getattr__=object.__getattribute__):
100101
self.__factory__
101102
)
102103

103-
__fspath__ = make_proxy_method(str)
104+
def __fspath__(self):
105+
wrapped = self.__wrapped__
106+
if isinstance(wrapped, string_types):
107+
return wrapped
108+
else:
109+
fspath = getattr(wrapped, '__fspath__', None)
110+
if fspath is None:
111+
return wrapped
112+
else:
113+
return fspath()
114+
104115
__reversed__ = make_proxy_method(reversed)
105116

106117
if PY3:

src/lazy_object_proxy/slots.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from .compat import PY3
55
from .compat import with_metaclass
66
from .utils import identity
7+
from .utils import string_types
78

89

910
class _ProxyMethods(object):
@@ -153,7 +154,15 @@ def __repr__(self, __getattr__=object.__getattribute__):
153154
)
154155

155156
def __fspath__(self):
156-
return str(self.__wrapped__)
157+
wrapped = self.__wrapped__
158+
if isinstance(wrapped, string_types):
159+
return wrapped
160+
else:
161+
fspath = getattr(wrapped, '__fspath__', None)
162+
if fspath is None:
163+
return wrapped
164+
else:
165+
return fspath()
157166

158167
def __reversed__(self):
159168
return reversed(self.__wrapped__)

src/lazy_object_proxy/utils.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
import sys
2+
3+
PY3 = sys.version_info[0] == 3
4+
5+
if PY3:
6+
string_types = str, bytes
7+
else:
8+
string_types = basestring,
9+
10+
111
def identity(obj):
212
return obj
313

tests/test_lazy_object_proxy.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1946,6 +1946,19 @@ def __init__(self, func, **lazy_attr):
19461946
assert not called
19471947

19481948

1949+
class FSPathMock(object):
1950+
def __fspath__(self):
1951+
return '/tmp'
1952+
1953+
19491954
@pytest.mark.skipif(not hasattr(os, "fspath"), reason="No os.fspath support.")
19501955
def test_fspath(lazy_object_proxy):
19511956
assert os.fspath(lazy_object_proxy.Proxy(lambda: '/tmp')) == '/tmp'
1957+
assert os.fspath(lazy_object_proxy.Proxy(FSPathMock)) == '/tmp'
1958+
with pytest.raises(TypeError) as excinfo:
1959+
os.fspath(lazy_object_proxy.Proxy(lambda: None))
1960+
assert '__fspath__() to return str or bytes, not NoneType' in excinfo.value.args[0]
1961+
1962+
1963+
def test_fspath_method(lazy_object_proxy):
1964+
assert lazy_object_proxy.Proxy(FSPathMock).__fspath__() == '/tmp'

0 commit comments

Comments
 (0)