Skip to content

Commit a836473

Browse files
committed
chore: mark steal_docs as deprecated in favor of wraps() or copy_docs()
The steal_docs api signature was fairly daft and py3k tooling allows doing this saner, so do so. copy_docs can handle both class and function, removing a ton of noise. In parallel to cleaning that out, convert to functools.wraps where appropriate, and drop docstrings where py3k now transfers annotations and docs across. Also do the full deprecation cleanup for this change also. Signed-off-by: Brian Harring <ferringb@gmail.com>
1 parent 37263cd commit a836473

File tree

8 files changed

+140
-115
lines changed

8 files changed

+140
-115
lines changed

src/snakeoil/containers.py

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from itertools import chain, filterfalse
1616

17-
from .klass import steal_docs
17+
from .klass import copy_docs
1818

1919

2020
class InvertedContains(set):
@@ -41,6 +41,7 @@ def __iter__(self):
4141
raise TypeError("InvertedContains cannot be iterated over")
4242

4343

44+
@copy_docs(set)
4445
class SetMixin:
4546
"""
4647
Base class for implementing set classes
@@ -52,47 +53,39 @@ class SetMixin:
5253
5354
"""
5455

55-
@steal_docs(set)
5656
def __and__(self, other, kls=None):
5757
# Note: for these methods we don't bother to filter dupes from this
5858
# list - since the subclasses __init__ should already handle this,
5959
# there's no point doing it twice.
6060
return (kls or self.__class__)(x for x in self if x in other)
6161

62-
@steal_docs(set)
6362
def __rand__(self, other):
6463
return self.__and__(other, kls=other.__class__)
6564

66-
@steal_docs(set)
6765
def __or__(self, other, kls=None):
6866
return (kls or self.__class__)(chain(self, other))
6967

70-
@steal_docs(set)
7168
def __ror__(self, other):
7269
return self.__or__(other, kls=other.__class__)
7370

74-
@steal_docs(set)
7571
def __xor__(self, other, kls=None):
7672
return (kls or self.__class__)(
7773
chain(
7874
(x for x in self if x not in other), (x for x in other if x not in self)
7975
)
8076
)
8177

82-
@steal_docs(set)
8378
def __rxor__(self, other):
8479
return self.__xor__(other, kls=other.__class__)
8580

86-
@steal_docs(set)
8781
def __sub__(self, other):
8882
return self.__class__(x for x in self if x not in other)
8983

90-
@steal_docs(set)
9184
def __rsub__(self, other):
9285
return other.__class__(x for x in other if x not in self)
9386

94-
__add__ = steal_docs(set)(__or__)
95-
__radd__ = steal_docs(set)(__ror__)
87+
__add__ = __or__
88+
__radd__ = __ror__
9689

9790

9891
class LimitedChangeSet(SetMixin):
@@ -147,7 +140,6 @@ def __init__(self, initial_keys, unchangable_keys=None, key_validator=None):
147140
self._change_order = []
148141
self._orig = frozenset(self._new)
149142

150-
@steal_docs(set)
151143
def add(self, key):
152144
key = self._validater(key)
153145
if key in self._changed or key in self._blacklist:
@@ -160,7 +152,6 @@ def add(self, key):
160152
self._changed.add(key)
161153
self._change_order.append((self._added, key))
162154

163-
@steal_docs(set)
164155
def remove(self, key):
165156
key = self._validater(key)
166157
if key in self._changed or key in self._blacklist:
@@ -173,7 +164,6 @@ def remove(self, key):
173164
self._changed.add(key)
174165
self._change_order.append((self._removed, key))
175166

176-
@steal_docs(set)
177167
def __contains__(self, key):
178168
return self._validater(key) in self._new
179169

@@ -201,23 +191,19 @@ def rollback(self, point=0):
201191
def __str__(self):
202192
return "LimitedChangeSet([%s])" % (str(self._new)[1:-1],)
203193

204-
@steal_docs(set)
205194
def __iter__(self):
206195
return iter(self._new)
207196

208-
@steal_docs(set)
209197
def __len__(self):
210198
return len(self._new)
211199

212-
@steal_docs(set)
213200
def __eq__(self, other):
214201
if isinstance(other, LimitedChangeSet):
215202
return self._new == other._new
216203
elif isinstance(other, (frozenset, set)):
217204
return self._new == other
218205
return False
219206

220-
@steal_docs(set)
221207
def __ne__(self, other):
222208
return not self == other
223209

@@ -260,6 +246,7 @@ def add(self, key):
260246
self._new.add(key)
261247

262248

249+
@copy_docs(set)
263250
class RefCountingSet(dict):
264251
"""
265252
Set implementation that implements refcounting for add/remove, removing the key only when its refcount is 0.
@@ -281,27 +268,24 @@ def __init__(self, iterable=None):
281268
if iterable is not None:
282269
self.update(iterable)
283270

284-
@steal_docs(set)
285271
def add(self, item):
286272
count = self.get(item, 0)
287273
self[item] = count + 1
288274

289-
@steal_docs(set)
290275
def remove(self, item):
291276
count = self[item]
292277
if count == 1:
293278
del self[item]
294279
else:
295280
self[item] = count - 1
296281

297-
@steal_docs(set)
298282
def discard(self, item):
299283
try:
300284
self.remove(item)
301285
except KeyError:
302286
pass
303287

304-
@steal_docs(set)
305-
def update(self, items):
288+
# the signature conflict is expected, thus the incompatible override.
289+
def update(self, items): # pyright: ignore[reportIncompatibleMethodOverride]
306290
for item in items:
307291
self.add(item)

src/snakeoil/data_source.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@
4343
)
4444

4545
import errno
46-
from functools import partial
4746
import io
47+
from functools import partial
4848

49-
from . import compression, fileutils, klass, stringio
49+
from . import compression, fileutils, stringio
5050
from .currying import post_curry
5151

5252

@@ -210,7 +210,6 @@ def __init__(self, path, mutable=False, encoding=None):
210210
self.mutable = mutable
211211
self.encoding = encoding
212212

213-
@klass.steal_docs(base)
214213
def text_fileobj(self, writable=False):
215214
if writable and not self.mutable:
216215
raise TypeError("data source %s is immutable" % (self,))
@@ -230,7 +229,6 @@ def text_fileobj(self, writable=False):
230229
raise
231230
return opener(self.path, "w+")
232231

233-
@klass.steal_docs(base)
234232
def bytes_fileobj(self, writable=False):
235233
if not writable:
236234
return open_file(self.path, "rb", self.buffering_window)
@@ -321,7 +319,6 @@ def _convert_data(self, mode):
321319
return self.data
322320
return self.data.decode()
323321

324-
@klass.steal_docs(base)
325322
def text_fileobj(self, writable=False):
326323
if writable:
327324
if not self.mutable:
@@ -337,7 +334,6 @@ def _reset_data(self, data):
337334
data = data.decode()
338335
self.data = data
339336

340-
@klass.steal_docs(base)
341337
def bytes_fileobj(self, writable=False):
342338
if writable:
343339
if not self.mutable:
@@ -354,7 +350,6 @@ class text_data_source(data_source):
354350

355351
__slots__ = ()
356352

357-
@klass.steal_docs(data_source)
358353
def __init__(self, data, mutable=False):
359354
if not isinstance(data, str):
360355
raise TypeError("data must be a str")
@@ -374,7 +369,6 @@ class bytes_data_source(data_source):
374369

375370
__slots__ = ()
376371

377-
@klass.steal_docs(data_source)
378372
def __init__(self, data, mutable=False):
379373
if not isinstance(data, bytes):
380374
raise TypeError("data must be bytes")
@@ -405,13 +399,11 @@ def __init__(self, data):
405399
"""
406400
data_source.__init__(self, data, mutable=False)
407401

408-
@klass.steal_docs(data_source)
409402
def text_fileobj(self, writable=False):
410403
if writable:
411404
raise TypeError(f"data source {self} data is immutable")
412405
return self.data(True)
413406

414-
@klass.steal_docs(data_source)
415407
def bytes_fileobj(self, writable=False):
416408
if writable:
417409
raise TypeError(f"data source {self} data is immutable")

src/snakeoil/formatters.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import typing
88
from functools import partial
99

10-
from .klass import GetAttrProxy, steal_docs
10+
from .klass import GetAttrProxy, copy_docs
1111
from .mappings import defaultdictkey
1212

1313
__all__ = (
@@ -201,7 +201,6 @@ def _write_prefix(self, wrap):
201201
# but it is completely arbitrary.
202202
self._pos = self.width - 10
203203

204-
@steal_docs(Formatter)
205204
def write(self, *args, **kwargs):
206205
wrap = kwargs.get("wrap", self.wrap)
207206
autoline = kwargs.get("autoline", self.autoline)
@@ -487,15 +486,14 @@ def __init__(
487486
self._fg_cache = defaultdictkey(partial(TerminfoColor, 0))
488487
self._bg_cache = defaultdictkey(partial(TerminfoColor, 1))
489488

490-
@steal_docs(Formatter)
489+
@copy_docs(Formatter.fg)
491490
def fg(self, color=None):
492491
return self._fg_cache[color]
493492

494-
@steal_docs(Formatter)
493+
@copy_docs(Formatter.bg)
495494
def bg(self, color=None):
496495
return self._bg_cache[color]
497496

498-
@steal_docs(Formatter)
499497
def write(self, *args, **kwargs):
500498
super().write(*args, **kwargs)
501499
try:
@@ -509,8 +507,8 @@ def write(self, *args, **kwargs):
509507
raise StreamClosed(e)
510508
raise
511509

512-
@steal_docs(Formatter)
513-
def title(self, string):
510+
def title(self, title: str): # pyright: ignore[reportIncompatibleMethodOverride]
511+
""" "Set the title"""
514512
# I want to use curses.tigetflag('hs') here but at least
515513
# the screen-s entry defines a tsl and fsl string but does
516514
# not set the hs flag. So just check for the ability to
@@ -519,7 +517,7 @@ def title(self, string):
519517
tsl = curses.tigetstr("tsl")
520518
fsl = curses.tigetstr("fsl")
521519
if tsl and fsl:
522-
self.stream.write(tsl + string.encode(self.encoding, "replace") + fsl)
520+
self.stream.write(tsl + title.encode(self.encoding, "replace") + fsl)
523521
self.stream.flush()
524522

525523

src/snakeoil/klass/__init__.py

Lines changed: 3 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"cached_hash",
2222
"cached_property",
2323
"cached_property_named",
24+
"copy_docs",
2425
"steal_docs",
2526
"ImmutableInstance",
2627
"immutable_instance",
@@ -36,7 +37,6 @@
3637
)
3738

3839
import abc
39-
import inspect
4040
from collections import deque
4141
from operator import attrgetter
4242

@@ -48,6 +48,7 @@
4848
immutable_instance,
4949
inject_immutable_instance,
5050
inject_richcmp_methods_from_cmp,
51+
steal_docs,
5152
)
5253
from .properties import (
5354
_uncached_singleton, # noqa: F401 . This exists purely due to a stupid usage of pkgcore.ebuild.profile which is being removed.
@@ -62,7 +63,7 @@
6263
jit_attr_named,
6364
jit_attr_none,
6465
)
65-
from .util import combine_classes, get_attrs_of, get_slots_of
66+
from .util import combine_classes, copy_docs, get_attrs_of, get_slots_of
6667

6768
sentinel = object()
6869

@@ -407,55 +408,6 @@ def __hash__(self):
407408
return __hash__
408409

409410

410-
def steal_docs(target, ignore_missing=False, name=None):
411-
"""
412-
decorator to steal __doc__ off of a target class or function
413-
414-
Specifically when the target is a class, it will look for a member matching
415-
the functors names from target, and clones those docs to that functor;
416-
otherwise, it will simply clone the targeted function's docs to the
417-
functor.
418-
419-
:param target: class or function to steal docs from
420-
:param ignore_missing: if True, it'll swallow the exception if it
421-
cannot find a matching method on the target_class. This is rarely
422-
what you want- it's mainly useful for cases like `dict.has_key`, where it
423-
exists in py2k but doesn't in py3k
424-
:param name: function name from class to steal docs from, by default the name of the
425-
decorated function is used; only used when the target is a class name
426-
427-
Example Usage:
428-
429-
>>> from snakeoil.klass import steal_docs
430-
>>> class foo(list):
431-
... @steal_docs(list)
432-
... def extend(self, *a):
433-
... pass
434-
>>>
435-
>>> f = foo([1,2,3])
436-
>>> assert f.extend.__doc__ == list.extend.__doc__
437-
"""
438-
439-
def inner(functor):
440-
if inspect.isclass(target):
441-
if name is not None:
442-
target_name = name
443-
else:
444-
target_name = functor.__name__
445-
try:
446-
obj = getattr(target, target_name)
447-
except AttributeError:
448-
if not ignore_missing:
449-
raise
450-
return functor
451-
else:
452-
obj = target
453-
functor.__doc__ = obj.__doc__
454-
return functor
455-
456-
return inner
457-
458-
459411
class SlotsPicklingMixin:
460412
"""Default pickling support for classes that use __slots__."""
461413

0 commit comments

Comments
 (0)